F#とScala F#勉強会メモ

昨日のF#勉強会の演習をScalaで組み直しコードを比べてみた。

F#のPartition関数をたたみ込み関数を使って作成する
自分のいつもの癖で関数型で考えるときはパターンマッチで再帰を使って考える癖がある。
そもそもこの時点で演習課題とずれているのだが、foldでいきなり考えられないのでまずはパターンマッチで考えた。

昨日は時間切れで完成せず家に帰って、中村さんの答えを参考に復習した。

F#バージョン

let partition (f:'a -> bool) (lst:'a list):('a list * 'a list) =
    let comp (list1,list2) v = 
        if(f v) then (v ::list1,list2) else (list1,v :: list2) 
    List.fold comp ([],[]) lst

これをScalaで書き直してみた

Scalaバージョン

def partition1[T](f:T=>Boolean,lst:List[T]):Pair[List[T],List[T]] = {
    def comp(tpl:Pair[List[T],List[T]],v:T):Pair[List[T],List[T]] ={
       if(f(v)) Pair(v :: tpl._1,tpl._2) else Pair(tpl._1,v :: tpl._2)
    }
    lst.foldLeft(Pair(List.empty[T],List.empty[T]))(comp)       
}

今回F#バージョンで型注釈を入れているがこれも省ける。
しかし、個人的にはパブリックな関数の型は定義した方がいいと思うのでF#バージョンでも型注釈をいれた。

この例でF#とScalaでの違いはcomp関数の型定義とタプルの型定義に現れている。
F#では型注釈でタプルを「( )」で表現できる。Scalaでは「( )」だけではタプルの型とは見なされない(はず...ひょっとして別の表現があるかも)
もう一点はF#では関数定義の型注釈も省けるのでcomp関数の型注釈が無い分簡潔になっている。
ネストした関数については型定義が省ける方が見通しがいいように思う。

この方式はListを使っているのでimmutableな処理方式であるが答えの並びは逆転する。

一方通常自分はどのように解決するかというとmmutableな処理方式で解決する。
単純に答えの形をつくりそこに要素を追加していく方式がイメージしやすいからである。

import scala.collection.mutable._
def partition2[T](f:T=>Boolean,lst:Buffer[T]):Pair[Buffer[T],Buffer[T]] = {
    val tpl = Pair(Buffer.empty[T],Buffer.empty[T])
    lst.foreach(v => if(f(v)) tpl._1 += v else tpl._2 += v)
    tpl
}

この方式は答えとなるtplを先に定義し、f関数の評価結果によってtplの1番目か2番目のArrayに追加する方式である。

F#勉強会では関数型でシンプルなコード作成を演習として行っている。
私はF#のライブラリをほとんど知らず力責めできないので、かえってこの演習が効果的である。

Scalaだと解決策が直ぐに見えるので関数型であろうがなかろうが、その解決策でコードを書いてしまう。最後のScalaのコードも直ぐに書けた。

F#だと手足をしばられてパズルを解いているようで窮屈だが、関数型で考えるトレーニングにはその窮屈さが効果的だ。