golang

お手本

コード

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

解説

ioutil.ReadDir(dir)

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

files, err := ioutil.ReadDir(dir)

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

file.IsDir()

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()※改変版

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の使い方

お手本の話に戻ろう。

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

break,continue,goto

ひとことで言えば。

コード意味備考
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
Last-modified: 2018-10-10 (水) 16:44:34