package main import ( "fmt" "io/ioutil" "path/filepath" ) func dirwalk(dir string) []string { files, err := ioutil.ReadDir(dir) if err != nil { panic(err) } var paths []string for _, file := range files { if file.IsDir() { paths = append(paths, dirwalk(filepath.Join(dir, file.Name()))...) continue } paths = append(paths, filepath.Join(dir, file.Name())) } return paths } func main() { fmt.Println(dirwalk("./")) }
ここでファイル一覧を取得してます。
files, err := ioutil.ReadDir(dir)
で、空っぽの文字列の列挙を作って、そこに入れるんだけど……ちょっと注意点が。
if file.IsDir() { paths = append(paths, dirwalk(filepath.Join(dir, file.Name()))...) continue } paths = append(paths, filepath.Join(dir, file.Name()))
あまり綺麗な書法とは思えない(まっとうな言語ならelseブロックを書くだろう。こういうのはC言語の悪癖だと思う)が、まぁ、わかると思う。
ここのキモは二点。
うーんやっぱり(あくまで個人的には)綺麗じゃない気がする。
お手本を少し改変してみますけど、どちらがいいかはあなた次第で。
if file.IsDir() { paths = append(paths, dirwalk(filepath.Join(dir, file.Name()))...) }else{ paths = append(paths, filepath.Join(dir, file.Name())) }
else{ } というのは elseブロックといって、いろんな言語で使われている。もちろんGOでも対応しているが、要は上の例だと「ディレクトリある場合はこれをせよ、で、 そうでない場合はこうせよ」と明示的に記述している事になる。
ちなみにお手本の方は明示的ではなく、単にディレクトリ処理に漏れた時に「それ以外」の処理をやる事になる。
改良版のほうが条件がきちんと明示されているので、わかりやすいとわたしは考えている。
ただしこの手の「わかりやすさ」は誰もが同じではないのも事実。
それに、 前の書き方のほうがコンパイル後の速度がわずかに速いというのなら、実はそちらが正しく、わたしが間違っている可能性もありうる。
お手本の話に戻ろう。
continueだけど、これは、この例だと直近の for文の頭に戻って続けなさいって事になる。
で、このあたりはセットで覚えてみましょう。
ひとことで言えば。
コード | 意味 | 備考 |
continue | この後の処理はやめてループに戻りましょ | Rubyでいうnext |
break | このループ抜けちゃいましょう | Rubyでいうbreak |
なんだけど、ここにラベルという概念が入るからややこしくなってる。
つまり。
コード | 意味 | 備考 |
continue | この後の処理はやめてループに戻りましょ | Rubyでいうnext |
break | このループ抜けちゃいましょう | Rubyでいうbreak |
continue(ラベルあり) | この後の処理はやめて指定ラベルのループに戻りましょ | ……。 |
break(ラベルあり) | 指定ラベルのループ抜けちゃいましょう | ……。 |
goto ラベル | 指定ラベルまで強制的にぶっ飛ばしましょ | ……。 |
……すごく古代BASICのGOTO文です、イヤすぎる。
まぁ、わたしはラベルを多用するような書き方はイヤだけど。(コード閲覧時に人間の視認性を高めるというのなら別だけど、処理に使いたくはない)
LABEL01: for i := 0; i < 10; i++ { for j := 0; j < 10; j++ { if i == 0 && j == 5 { // for(i)へcontinue continue LABEL01 } else if i == 1 && j == 1 { // for(j)へcontinue continue } } }
今回の例をRubyに置き換えてみると、こんな感じになりますかね?
(0..9).each{|i| (0..9).ech{|j| if i == 0 && j == 5 break elsif i == 1 && j == 1 next end } }
たぶんRuby書き的には、continue ラベル より break を選びそうだなと思います。ラベルを使うのは古代BASICの gotoの悪夢の再現を意味しかねないし。
ちなみにgoto文はさらに。
for j := 0; j < 10; j++ { if j == 5 { // 条件そろったから逃げ出そう。 goto LABEL01 } } LABEL01:
使い方はわかるのだけど。
正直、こんなgoto多用するプログラマって…… ><