LoginSignup
12
9

More than 3 years have passed since last update.

F#楽しいですよ

Last updated at Posted at 2020-02-12

こんなこと言ってる自分も勉強中の一人だけど。
でももっとF#の知名度上がったらいいなーと思って、Twitterとかもくもく会とかでF#の事言いまくってる。

楽しいところ

F#はパズル組んでるみたいで書いててとても楽しい。
定数しか使えないという制約の元で、いかに読みやすくバグなく書けるか、めっちゃ頭をひねるので。1

高階関数と「|>」を使うとさらっと書けて、読みやすいコードが書ける。2
ポーカーの役判定を高階関数縛りで書いてた時とかも超楽しかった。

あとは以下みたいな悪しきコードが、すばらしく簡潔になったりしたときの感動はひとしお。3


// 特定の要素を取り除いた新しいリストを取得したい
// ※player.HandCardsはCard型のlistである
let askCardIndex =
(player.HandCards |> List.tryFindIndex(fun x -> (isKnown askCard x))).Value

List.append player.HandCards.[.. askCardIndex - 1] player.HandCards.[askCardIndex + 1 ..]

// ↓↓↓ 以下の1行だけで実現できる ↓↓↓
player.HandCards |> List.except [askCard]

F#について詳しく知りたい方は以下の記事が非常に良いと思います。
F# を知ってほしい
仕事と F# と私

ローカルの実行環境

個人的には.NET Core + vscode + ionide(プラグイン)でいいと思う。
大事なのは最新版のdotnetcoreを入れる事(執筆時点では3.1.1)。
2.1とか古いままにしてると、以下のionideの超嬉しい機能が使えなかったりする。

ionideは各行で型評価して出力してくれる
↓↓↓
D584EC29-ED44-4B55-9804-EECFBE943646.png

冗長なコードをこう書き替えたら?とか聞いてくるのでとても賢い。
↓↓↓

修正案の例
// 書いてたやつ
[1..10] |> List.sortBy(fun _ -> Guid.NewGuid()) |> List.head

// ionideが提示した修正案
[1..10] |> List.minBy(fun _ -> Guid.NewGuid())

オンライン開発環境

いろんな事情があってローカルで環境構築できない場合は、ここがインテリセンス効くので最強。
https://fable.io/repl/

記事の執筆時点当初では、まだ全機能がリリースされてるわけではなかったけど
メンバーの方が「リリースしたよ!」って報告してくれました。
Thanks!
https://github.com/fable-compiler/repl/issues/105
↓↓↓
ただ、Console.readLine()とか、どうしても一部動かせない処理は存在する。

こちらはインテリセンスは効かないけど、4.0相当の機能は確実に実行できる。
https://repl.it/languages/fsharp

てなわけで、fable.ioでコーディングして
実行できない関数にぶちあたってしまった場合はrepl.itでコンパイルすると吉。

ドキュメント

msdnはドキュメントが古い。でも一覧性はとても良い。
なのでインテリセンスでどんな候補があるか見て、気になったやつをググるが確実っぽい。

自分はListとかの高階関数使うので、見るのはこれ。
こんなにあった... F#ドキュメントには未掲載のArray関数
これは知らなかった... F#ドキュメントには未掲載のコレクション関数(List編)

あとは人が書いたF#のコード眺めてると、未知の記法がある事に気付いて輸入できたりする。

O'ReillyのプログラミングF#を読みながら、徐々に理解を広げていってる感じ。

個人的に詰まったところの覚え書き(主に文法面)

型の注釈をどう入れるか

  • リストはType名 listの形で定義
// OK
let buildPlayer (handCards:Card list) =
    ...

// NG(別モノになってしまう)
let buildPlayer (handCards:List<Card>) =
    ...

  • 複数の引数に注釈入れる場合は()でそれぞれ囲む
// OK
let phase (player:Player) (enemy:Player) =
  ...

// 文法的にはOKだけどこれだとタプルを引数でもらうことになってしまう(カリー化できない)
let phase (player:Player, enemy:Player) =
  ...

// NG(シンタックスエラー)
let phase player:Player enemy:Player =
  ...

// OK 片側だけならこんな感じ
let rebuildAskPlayer (player:Player) askAfterHandCards =
 ...

意味のない変数をどうするか

  • _を使うと警告がでない

let shuffledCards =
  cardList
  |> List.sortBy(fun _ -> Guid.NewGuid())

タプルの戻り値

  • まとめてletできる
// xには1が yには7が
let x, y = (1, 7)

たまに戻り値で返ってくるOptionってなに

  • Some(値)かNoneが入ってる
// 値があるならSomeの中にValueがある
let x = Some(1)
printfn "%A" x.Value
// => 1

// Noneを明示的に入れたらインテリセンスが.Valueを候補として出してくれない。賢い
let y = None
printfn "%A" y
// => null

ifは式

  • 分岐で得られた値を束縛できる
let result = if playerCard.Number > enemyCard.Number then High else Low
// => resultはHighかLowが束縛される

  1. 他の関数型言語でも同じやろうけど 

  2. 個人差あり、最初は慣れが必要かも 

  3. インデックスアクセス怖い。でもList.removeがないと知ったときは、これしか思いつかなかった 

12
9
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
9