自転車とプログラミング

自転車メーカーに勤める会社員がプログラミングを学ぶ中で感じたことを書きます。最近サービス作りました。

人のプログラムを読解しよう lsコマンド編01

フィヨルドブートキャンプに所属しているYukiです。 今回はフィヨルドブートキャンプで課された「lsコマンドを作る」という課題を終了し、他の受講生の解答が見られるようになりましたので、その読解に取り組みます。

ブログにまとめるのは引っかかった箇所だけ、ということで。

**引数

メソッド引数に**argと記述することでキーと値のセットを受け取ることができる。 変数argにキーと値のハッシュが生成される。 メソッド定義の中ではarg[キー]の記述で値を取り出すことができる。

ハッシュそのもののメソッドへの引き渡しは通常のメソッド引数で行うことができる。

getoptsによるオプション処理

OptionParser#getoptsgetopts(argv, *opts)は引数をパースした結果を、Hash として返す。 optsで指定した文字列がコマンドラインオプションに指定されると、その文字列をキー、true/falseや続けて指定したオプションに応じた値でもってハッシュとなる。記述例が以下。

opt = OptionParser.new
params = opt.getopts(ARGV, "ab:", "foo", "bar:")
# params["a"] = true   # -a
# params["b"] = "1"    # -b1
# params["foo"] = true  # --foo
# params["bar"] = "x"  # --bar x

オプションが指定されればtrueだし、そうでなければfalseなのでそれでもってオプション指定の判定をするとスムーズ。 getopts('abc')とすれば-a -b -cでも-abcでも-cabでも対応してくれる。

私が作ったオプション処理はopt.onを使ってオプションごとの値を設定していたので冗長だった。

opt = OptionParser.new
params = {}

opt.on('-l') { |l| params[:l_option] = l }
opt.parse!(ARGV)

一行で済む処理はopt.onでもいいかもしれないが、そうではなかったのでtrue or falseで条件分岐に使用するだけで済むケースといえる。

Dir.globによるファイルパスの取得

self[*pattern, base: nil, sort: true] -> [String] パターンにマッチするファイル名を文字列の配列として返すメソッド。 Dir.glob('*')とするとディレクトリ内のファイルを返す。 Dir.glob("*", File::FNM_DOTMATCH)でカレントディレクトリや一個うえのディレクトリも取得できる。

パターンだけじゃなくてベースディレクトリの指定もできる。 ファイルパスを取得するディレクトリを指定することはできなさそう。 私の場合はディレクトリ指定も実装するためにDir.entriesを使った。

連鎖的なメソッド処理 / プログラムの構成の考え方

これは読解を通じて気づいた部分。 これまで作ってきた自分のコードはmainメソッドを定義せずに、メインの処理は直書き…という形を取ってきた。そのやり方自体が初心者っぽいというかコードの見通しの悪さにつながっているような気がする。

メンターさんのコードはmainメソッドを定義してそこから数珠つなぎのように、メソッドからメソッドを渡って処理が続いて行く構造だった。直書きしているのはメインメソッド実行のみ…という簡潔さ。

この読解に先行して別のコマンドを作る課題に取り組んでいたが、直に記述する箇所とメソッドとで区別がしきれずにとっちらかったコードになってしまった。これは、どちらに記述すべきかの基準があやふやな上に、そのあやふやな基準の上でロジックを組み立てたことが原因と言える。

まずはmainメソッドを立てる方式にする、という形で今後やっていきたい。

コードの整理整頓

これも読解をつうじて気づいた部分。多分、これをやると可読性が上がりそう…というところが3個。 一つはハッシュを使うということ。やっぱりキーと値の組み合わせが可読性の点で優秀。キーがタイトルになってくれるおかげで何を扱っているか、が非常にわかりやすい。配列のように添え字になるととたんに「なにをあつかってるか」がわかりづらくなってしまう。

もう一個はハッシュに加えてformat文字列を組み合わせること。format文字列もキーを使って値を呼び出せるので見た目にわかりやすい。書くのは大変そうだけど。

あとは改行していくこと。 これはハッシュでも配列でもそうなんだけれど,のたびに改行するだけでめちゃくちゃ見通しが良くなる。 format文字列が難しいならヒアドキュメントをつかって出力する文字列を改行していくのもあり…か? ヒアドキュメント使うと実際の出力も改行されちゃうから微妙か。

hash.mapによる値の抜き出し。

複数のハッシュが格納された配列に対してmapメソッドを使うことで特定のキーに対応する値だけの配列を作る、というアプローチ。ハッシュを使う上で必須か。

test_hash = [ { a:100, b:200, c:300, d:400 }, { a:500, b:600, c:700, d:800} ]

arry = test_hash.map{ |val| val[:a] }
=> [ 100, 500 ]

といった具合。

sprintfとprintfの違い

メンターさんのコードの中では使い分けがされていた2つのメソッド。 そんな大きな違いはなくて、 sprintfは引数をフォーマットした文字列が返り値になり、 printfはフォーマットした文字列をport($stdout)に出力する。

以上、lsコマンドの読解でした。