#author("2018-10-10T16:44:34+09:00","default:yakumo_murakami","yakumo_murakami")
[[golang]]

* お手本 [#c545996e]

- https://qiita.com/tanksuzuki/items/7866768c36e13f09eedb

* コード [#d9de5b46]

 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("./"))
 }


* 解説 [#u63431d5]

** ioutil.ReadDir(dir) [#d23d792c]

ここでファイル一覧を取得してます。

 files, err := ioutil.ReadDir(dir)

で、空っぽの文字列の列挙を作って、そこに入れるんだけど……ちょっと注意点が。

** file.IsDir() [#d745319e]

 if file.IsDir() {
   paths = append(paths, dirwalk(filepath.Join(dir, file.Name()))...)
   continue
 }
 paths = append(paths, filepath.Join(dir, file.Name()))

あまり綺麗な書法とは思えない(まっとうな言語ならelseブロックを書くだろう。こういうのはC言語の悪癖だと思う)が、まぁ、わかると思う。

ここのキモは二点。

- そのファイル名が実はディレクトリなら、自分自身(ファイル一覧取得)を呼び出しその結果を追加して次へ。
- 上記条件以外は、該当ファイルを追加。

うーんやっぱり(あくまで個人的には)綺麗じゃない気がする。~
お手本を少し改変してみますけど、どちらがいいかはあなた次第で。

** file.IsDir()※改変版 [#i1a83106]

 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の使い方 [#y19b258f]

お手本の話に戻ろう。

continueだけど、これは、この例だと直近の for文の頭に戻って続けなさいって事になる。~
で、このあたりはセットで覚えてみましょう。

*** break,continue,goto [#b9b3a2ba]

ひとことで言えば。

,コード,意味,備考
,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多用するプログラマって…… ><

トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS