RSS

Haskell がおもしろかったので 勢い余って F# と比較してみた。


[haskell]
— リスト内包表記
qsort1 :: (Ord a) => [a] -> [a]
qsort1 [] = []
qsort1 (x:xs) =
let sm = [y | y <- xs, y < x]
bg = [y | y <- xs, y >= x]
in qsort1 sm ++ [x] ++ (qsort1 bg)

— filter
qsort2 :: (Ord a) => [a] -> [a]
qsort2 [] = []
qsort2 (x:xs) =
qsort2 (filter (< x) xs) ++ [x] ++ (qsort2 (filter (>= x) xs))

— filter+関数内関数
qsort3 :: (Ord a) => [a] -> [a]
qsort3 [] = []
qsort3 (x:xs) =
let sm = filter (< x) xs
bg = filter (>= x) xs
in qsort3 sm ++ [x] ++ (qsort3 bg)
[/haskell]


で、F# 。
思ったより差があった。型宣言とか使うとごちゃごちゃしそうだなぁ。
毎回 letとか ;;とか、ghciで書いてるみたい。 改行はできるけど。

パターンマッチするには match式を使う。ただmatch式だと引数とる必要があるので、省略したい場合は functionがいる。
要はHaskellで
fn a = (+) 1 a

fn = (+) 1
って書くためにキーワードが変わるみたいな雰囲気。
単なるパターンマッチの開始のためにmatch/functionが必要で、その後ガードみたいな書き方になる。

[fsharp]
// リスト内包表記 – pythonぽい。
let rec qsort1 = function
| x::xs -> qsort1 [for y in xs do if y < x then yield y] @ [x] @ qsort1 [for y in xs do if y >= x then yield y]
| _ -> [];;

// filter あと matchつかった
let rec qsort2 ls =
match ls with
| x::xs -> qsort2 (List.filter (fun a -> (a < x)) xs ) @ (x::(qsort2 (List.filter (fun a -> (a >= x)) xs )))
| _ -> [];;

// filter+関数内関数
let rec qsort3 = function
| x::xs ->
let sm = List.filter ((>=) x) xs // 前置関数扱いだからこうなる。
let bg = List.filter ((<) x) xs
in qsort3 sm @ [x] @ (qsort3 bg)
| _ -> [];;

[/fsharp]

Haskellの時と違って型指定してないのでリストじゃないかもしれないから _ -> [] にしておいた。

Haskellだと部分適用で(< x)を渡せていたが、F#だと>=を 2引数関数として渡すから逆になる。
Haskellなら ? < x という状態で左辺に埋めろって事になるのに、
F#だと >= x ?x>=? こういう扱い。だから不等号が逆。
あぁ、今気が付いたけどイコールはbg側につけておけばよかった。

部分適用か中置関数として(x (<))みたいに渡せればいいんだけど、2引数関数だといまいち直感的じゃないなぁ。

F#は(Haskellも)初心者なのでもっとうまい書き方があるのかもしれない。

    • いげ太
    • 2013 7/7 10:13pm

    > 毎回 letとか ;;とか、
    毎回 ;; は必要ないです。;; は、fsi に対話的に入力する場合に、fsi に評価するタイミングを伝える目的で使用します。ので、ファイルにコードを書いて fsc なり fsi に食わせる場合はなくて OK です。

    > 型指定してないのでリストじゃないかもしれないから _ -> [] にしておいた。
    型推論によって、パターン マッチ対象の値がリスト型であることが決まります。[] – [] でよいです。

    > Haskellだと部分適用で(=を 2引数関数として渡すから逆になる。
    確かに、僕もたまに Haskell のように書ければと思うことがありますね。

    キャッチーなクイック ソートのコード例ですが、Haskell の ++ なり、F# の @ なりといったリスト連結をバリバリ使ったコードはあまりおすすめできません、遅いので。

    詳しい話は「haskell quicksort 遅い」あたりでググればたくさん出てくるかと思いますので、そちらに譲ります。

      • flied_onion
      • 2013 7/12 8:12am

      コメントありがとうございます。参考になります。

      F#でもREPLでなければletや;;は不要なんですね。ありがとうございます。

      型指定は参考にしていたときに、[]=[]_=[]と両方見かけたので勘違いしてしまいました。x::xsで推論されるということですね。

      Haskellの++が遅くて、Lispのconsにあたる : を使った方がパフォーマンスが良いのは本で見て覚えていました。
      F#で何になるのかは自分で調べてみます。

      >> Haskellだと部分適用で(=を 2引数関数として渡すから逆になる。
      > 確かに、僕もたまに Haskell のように書ければと思うことがありますね。

      この記事を書いた後で OCamlではどうなのか見ていたら無理矢理やっているというコードは見かけました。
      F#で可能かどうかはわかりませんが、あると便利ですよね。F#の |> はHaskellに輸入されたというのをどこかで見かけましたし、
      F# も部分適用とか採用してお互いに便利になってほしいですね。

  1. トラックバックはまだありません。

*