前回のエントリの「標準入力から文字列を読み取り先頭から連続する数字だけを抜き出すプログラム」を
Collections.Seq 系関数で書き換えてみる。
takeWhile 関数が適しているだろう。
getNumIndex 関数を以下で置き換える。
Seq.takeWhile(fun s -> Char.IsNumber(s)) line
コレクションのままでは文字列として標準出力できないので、toArray 関数で配列に変換する。
Seq.takeWhile してから Seq.toArray すると、以下のいずれかのように書くようになるが、
// 一旦 takeWhile の結果を変数束縛してから toArray (ちょっと冗長…)
let numSeq = Seq.takeWhile(fun s -> System.Char.IsNumber(s)) line
Seq.toArray numSeq
// line に先に takeWhile を適用してから toArray (ちょっと読みづらい…)
Seq.toArray (Seq.takeWhile(fun s -> System.Char.IsNumber(s)) line)
ここでパイプライン演算子(|>)を使用して左から右に流れるように関数を適用するようにしてみる。
line |> Seq.takeWhile(fun s -> System.Char.IsNumber(s)) |> Seq.toArray
ちなみに関数合成演算子(>>)を使用すると以下のようになるが、
line |> (Seq.takeWhile(fun s -> System.Char.IsNumber(s)) >> Seq.toArray)
ここはパイプライン演算子で次々に関数を適用していく書き方のほうが可読性が高そう。

最終的にこんな感じになった。
また、別パターンとして tryFindIndex で最初の数字ではない文字のインデックスを取るとこんな感じ。
findIndex 関数では全て数字だった場合に KeyNotFoundException が上がるので、
tryFindIndex 関数で option を返すようにした。

以下、移転前のブログにいただいたコメントを転載させていただきます。
引数でラムダ式を渡すときに fun x -> foo x というのは foo だけに短縮できます。
Seq.takeWhile(fun s -> Char.IsNumber(s))
  ↓
Seq.takeWhile(Char.IsNumber)
七誌 (id:n7shi)
コメントありがとうございます。
おお、なるほど。ずいぶんスッキリするんですね。
そういえばScalaでも
list.takeWhile{x => foo(x)}
  ↓
list.takeWhile(foo)
という書き方ができました。
ザネリ (id:zaneli)

Copyright© 2011-2021 Shunsuke Otani All Right Reserved .