はてなの金次郎

とあるエンジニアの技術系ブログ

GitLabのイシュートラッカー活用術

はじめに

gitlab-jp.connpass.com

「GitLab Meetup Tokyo #7: 新年度応援&GitLab 11.0」で人生初LTをしてきました。

私たちは100回の勉強会参加よりも1回の登壇へのチャレンジの方が より大きな成長ができると信じています。

by JAWS-UG

たった5分間のLTでしたが、ただ勉強会に参加するよりもずっと大きな物を得られました。

今後もLTのチャンスがあれば積極的にやっていきたいです。

スライド

「GitLabのイシュートラッカー活用術」というタイトルでGitLabのイシュートラッカーを使いやすくするための機能を紹介してきました。

発表資料はこちらです。

speakerdeck.com

スライドの内容

背景

  • 社内でGitLabとBacklogを併用しているプロジェクトがあった。
  • GitLabはエンジニアが主にソースコードの管理として利用していた。
  • BacklogはPMと一部のエンジニアなど限られた人がイシューを管理していた。
  • GitLabでも実現できるのになぜ併用しているのか疑問に思っていた。
  • GitLabのビジョンを知りGitLabに統一して使った方が効果が高いことがわかった。

GitLabのビジョン

「誰しもが全てのデジタルコンテンツを共有できるようにし、チームが効果的に協力しあいよりよい成果をより早く達成できるようにする」

まとめ

  • 実際にGitLabに統一しGitLabの機能を活用することで以下の効果があった。
    • チーム内コミュニケーションが活発になる。
    • チーム全員がプロジェクトのイシューとその解決課程が共有されている。
    • より早くイシューが解決されるようになる。
  • GitLabのビジョンを知りどう使うべきなのかを考えてみる。
  • イシュートラッカーを便利に使うための機能がたくさんある。
    • Issue Board
    • Issue Label
    • Slack Notifications Service
    • Description templates
    • External issue tracker
  • 「GitLab実践ガイド」を読もう!

Twitterの反応

嬉しい反応をいただけました。ありがとうございます!

複雑なJSONから特定のデータを再帰で取り出せるようになるための4ステップ

はじめに

qiita.com

こちらの記事の「複雑なJSONから特定のデータを取り出す」実装するにあたり、段階的に考えることで徐々に正解に近づけていきました。もし、上記の記事だけではわかりにくかったり、もう少し詳しい説明が読みたかったりする場合はぜひ参考にしてみてください。

目次

Step1. 配列からStr型の要素を取得する

配列の要素がStr型であればresに追加を配列の要素個数回繰り返します。

サンプルデータ

sample = ["a", "b", 1]

取得したい値

["a", "b"]

実装

res = []
for v in sample:
    if isinstance(v, str):
        res.append(v)
        
print(res) # ["a", "b"]

forを用いた反復法です。Step2以降との比較のためにリスト内包表記では実装していません。 非常にシンプルな例なので解説は不要かと思います。

Step2. 2階層構造の配列からすべてのStr型の要素を取得する

次は、データが2階層、つまり、配列の要素に配列がある場合を考えてみましょう。

サンプルデータ

sample = [["a", 1], "b"]

取得したい値

["a", "b"]

実装

res = []
for arg in sample:
    if isinstance(arg, str):
        res.append(arg)
    if isinstance(arg, list):
        for v in arg:
            if isinstance(v, str):
                res.append(v)

print(res) # ["a", "b"]

Step1の実装を元に2階層のfor文で実装しました。

見て分かる通り、期待の結果は得られますが、Str型かどうかの条件分岐を2回行なっており冗長であることがわかります。

この実装だと配列が3階層、4階層、、、と増えた場合、その分for文の階層を増やすことになり、さらに冗長なコードになってしまいます。

そこで再帰関数の登場です。

再帰は、大きな問題を小さな問題に分割して解決する分割統治法 であることが特徴で、Step2で実装したコードを再帰関数で実装すると冗長な部分を簡潔に実装することができます。Step3でやってみましょう。

Step3. n階層構造の配列からStr型の要素を取得する

Step2の実装を再帰法で実装してみます。再帰にまだ自信がないという方は、Step3の実装に入る前にこちらの記事を参考にしてみてください。

jumpyoshim.hatenablog.com

サンプルデータ

sample = ["a",["b", 1, [[["c", 2], 3], 4], "d"], ["e"]]

取得したい値

["a", "b", "c", "d", "e"]

実装

def get_str(arg):
    res = []
    if isinstance(arg, str):
        res.append(arg)
    elif isinstance(arg, list):
        for v in arg:
            return res += get_str(v)

print(get_str(sample)) #

処理を順番に追いながら解説していきます。 sample はリスト型なので、最初の処理は、

for v in arg:
    res += get_str(v)

となります。この時、arg は3要素なので、上記の処理は下記の処理をそれぞれ行うことになります。

①res += get_str("a")
②res += get_str(["b", 1, [[["c", 2], 3], 4], "d"])
③res += get_str(["e"])

それぞれの処理をさらに追ってみましょう。

①res += get_str("a")

これは、res"a" を追加して終了です。

②res += get_str(["b", 1, [[["c", 2], 3], 4], "d"])

こちらは、下記のそれぞれの処理を行います。

res += get_str("b")
res += get_str(1)
res += get_str([[["c", 2], 3], 4])
res += get_str("d")

これらもそれぞれ処理を追うことができます。

③res += get_str(["e"])

そしてこちらは、下記の処理を行います。

res += get_str("e")

同じようにどんどん処理を追ってくことができます。

①res += get_str("a")
>> res.append("a") *
②res += get_str(["b", 1, [[["c", 2], 3], 4], "d"])
>> res += get_str("b")
   >> res.append("b") *
   res += get_str(1)
   res += get_str([[["c", 2], 3], 4])
   >> res += get_str([["c", 2], 3)
      >> res += get_str(["c", 2])
         >> res += get_str("c")
            >> res.append("c") *
            res += get_str(2)
      res += get_str(4)
   res += get_str("d")
   >> res.append("d") *
③res += get_str(["e"])
>> res += get_str("e")
   >> res.append("e") *

*印のところがresに最終的に追加されている部分です。そして、最終的に get_strres をreturnするため、Str型の値のみが格納された配列を取得することができます。

このように get_str の引数がStr型であれば配列に追加され、List型であればさらにその要素に対して get_str (関数自身)を呼ぶことで、再帰的に配列の要素を処理してくれる関数が実装できました。

Step2のような反復法だと、n階層分forを書く必要があるのに比べ、コードの量が少なく簡潔に実装できていることがわかります。

Step4. Int,Str,List,Dictの混合オブジェクト(JSON)からStr型のvalueを取得する

ついに目的の実装ですが、Step3まで終えた皆さんは簡単に実装できると思います。

Step3の実装ではList型かStr型かの判定しか行っていませんでしたが、そこにDict型の判定が加わるだけです。

サンプルデータ

sample = {
    "a": [{
        "b": "y", 
        "c": [{
            "d": [2,3]
        }], 
        "e": {"g": "z"}
        }],
        "f": ["x"],
}

取得したい値

["x", "y", "z"]

実装

def get_str(arg):
    res =[]
    if isinstance(arg, str):
        res.append(arg)
    elif isinstance(arg, list):
        for item in arg:
            res += get_str(item)
    elif isinstance(arg, dict):
        for value in arg.values():
            res += get_str(value)
    return res

print(get_str(sample)) # ["x", "y", "z"]

Step3にDict型が来た場合の処理を追加します。 Dict型の場合は、キーバリューのバリューを get_str の引数に与えてあげるだけですね。

これでJSONから特定のデータを取り出せることができました!

リファクタ

Step4の実装だと他のデータを取り出したい時に関数を書き換えなければいけないため、データの判定は外出しすることにします。 こうすることで、他のデータを取り出したい時にそのデータを判定する関数を実装するだけで済みます。

def search(arg, cond):
    res =[]
    if cond(arg):
        res.append(arg)
    elif isinstance(arg, list):
        for item in arg:
            res += search(item, cond)
    elif isinstance(arg, dict):
        for value in arg.values():
            res += search(value, cond)
    return res
    
def has_str(arg):
    return isinstance(arg, str)

def get_str(arg):
    return search(arg, has_str)

Pythonで再帰関数を理解するための最も簡単な例

はじめに

qiita.com

こちらの記事で複雑なJSONから特定のデータを取得するために再帰関数で実装したのですが、初めは再帰関数なにそれ状態から、最後にはしっかりと理解して実装できるようになりました。

そこで今回は、私が再帰関数を理解するにあたり一番最初に先輩社員に教えていただいた再帰関数の例をご紹介します。

目次

再帰関数を実装する前に

再帰関数を実装する前に、再帰関数を用いる 理由ルール に関して知っておくと、理解が早く進むのではないかと思います。

再帰関数を用いる理由とルールに関しては、独学プログラマーがわかりやすいので引用させていただきます。

まず、再帰関数を用いる理由に関してはこう説明されています。

再帰は、大きな問題を小さな問題に分割して解決する分割統治法で使われる手法で、 小さな問題は比較的楽に解決できるだろう、という点に着目しています。 (中略) 反復法で解決できるどんな問題も、再帰法に置き換えられます。 その上、再帰法はより洗練された解決法となることがあります。

そして、再帰のルールに関してです。

  1. 再帰法は、再帰終了条件を持たなければならない。
  2. 再帰法は、状態を変えながら再帰終了条件に進んでいかなければならない。
  3. 再帰法は、再帰的に関数自身を呼び出さなければならない。

詳細に関しては実際に独学プログラマーを読んでみてください。

再帰関数の最もシンプルな例

再帰関数を勉強するにあたり、個人的に一番シンプルでわかりやすいと感じた再帰関数の例です。

def sum(n):
    if n <= 0:
        return n
    return n + sum(n-1)

名前があまり良くないですが、1からnまでの自然数の和を返す関数です。 例えば、n=10だった場合、sum関数の戻り値は、

10 + sum(10-1)

sum(10-1) = sum(9)の戻り値は、

9 + sum(9-1)

sum(9-1) = sum(8)の戻り値は、、、、、

のようにsum関数は自分自身を呼び出し続けます。 そして、n=0のとき、

if n <= 0:
    return 0

0を返します。nが0以下がこの再帰関数の再帰終了条件にあたります。

以上からsum(10)の返り値は、

10 + 9 + 8 + ... + 1 + 0 = 55

となります。このように再帰関数は、状態を変えながら関数自身を再帰的に呼び続けることがわかります。

おわりに

再帰関数は、プログラミング初学者にとってはすごく難しい概念だと思うので、理解のお役に立てれば嬉しいです。