学習の記録−10
今度は
の本の内容を適当にメモ。
open System (* 複数行コメント *) [<EntryPoint>] let main(args : string[]) = ///斜線3つで書いたコメントはIDEの補完で表示される let greeting, thing = args.[0], args.[1] let timeOfDay = DateTime.Now.ToString("hh:mm tt") printfn "時刻 %s : %s, %s" timeOfDay greeting thing 0
これをF# Interactiveに送って実行すると
> main[|"Hello"; "World"|];; 時刻 11:25 午前 : Hello, World val it : int = 0
他の言語と違ってソースファイルをコンパイルする順番が大事。既にコンパイル済みor同一ファイル内で定義済みのものしかつかえないのでファイルの順序をただしく依存関係の低い順に並べておくこと。
ignore関数は関数の返り値を無視してunitを返してくれる。
> let f x = x*x;; val f : int -> int > f 4;; val it : int = 16 > ignore(f 4);; val it : unit = ()
複雑なリスト内包表記も書けるのか。
let x = [ let negate z = -z for i in 1..10 do if i % 2 = 0 then yield (negate i) else yield i];; >val x : int list = [1; -2; 3; -4; 5; -6; 7; -8; 9; -10]
名前空間とモジュールの違い
- 名前空間はネスト不可・同一ファイルに複数定義できるが、型の”宣言”のみで値の定義はできない。
- モジュールはネスト可能・同一ファイルに一個(ファイルの先頭)、値の定義も可能。
[]属性について
F#ではデフォルトとしてVisual Studioの最後に追加されているファイルが自動的にメインファイルとして実行されるんで、それが嫌な時には[
- string型の引数を1つとる
- intを返す
- 最後にコンパイルされるファイル内に定義された最後の関数であること
の条件を満たす必要がある。
記号演算
C++でいう所の演算子のオーバーロード的なこともできる(実際はただの関数だそうで)。例として階乗の計算を定義。
let rec (!) x = if x <= 1 then 1 else x * !(x-1);; > !5;; val it : int = 120
引数を2つ以上取る場合はデフォルトで一番目の引数が記号演算の左辺になる。
let (====) x y = if x = y then Some(true) else None;; > 1 ==== 1;; val it : bool option = Some true > 1 ==== 2;; val it : bool option = None
関数合成
順次合成演算というようで”>>”を使う。手前にあるもの(左側にあるもの)から順に作用されるようになる。
> let f = (fun x -> 2*x) >> (fun x -> x+1);; val f : (int -> int) > f 10;; val it : int = 21
その逆版の"<<"もあり。
名前付きパターンと[]属性
マッチさせるときに定数じゃなく可変な名前付変数にする場合の書き方。
let hello name = match name with | "Taro" -> "Hello, Taro!" | x -> sprintf "Hello, %s. How are you?" x;; > hello "Taro";; val it : string = "Hello, Taro!" > hello "teramonagi";; val it : string = "Hello, teramonagi. How are you?"
もし、定数をこの関数の外で指定したい場合には[
[<Literal>] let Bob = "Bob san"; let hello_mod name = match name with | "Taro" -> "Hello, Taro!" | Bob -> "Hi, Bob!" | x -> sprintf "Hello, %s. How are you?" x;; > hello_mod "Taro";; val it : string = "Hello, Taro!" > hello_mod "Bob san";; val it : string = "Hi, Bob!" > hello_mod "teramonagi";; val it : string = "Hello, teramonagi. How are you?"
whenガードとパターンのグループ化
マッチの際に条件付けたりorっぽくしたりとこんなんもできますよと。
let func z = match z with | 1 | 2 | 3 -> 100 | x when x < 10 -> 10 | _ -> 1 ;; > func 2;; val it : int = 100 > func 9;; val it : int = 10 > func 2303;; val it : int = 1
パターンマッチ使ってリストの長さを計算する
let rec listLength x = match x with | [] -> 0 | [_] -> 1 | [_; _] -> 2 | hd::tail -> 1 + listLength tail;; > listLength [];; val it : int = 0 > listLength [1];; val it : int = 1 > listLength [1;2;3];; val it : int = 3 > listLength [1..5];; val it : int = 5
判別共有対とレコード
判別共有体とレコードは前者がenumっぽくて、後者が構造体っぽいもの。どちらもtypeを使って宣言する。
レコードのパターンマッチ
こんなかんじで。実行するとTypeがワゴンのもののみ取得される。
type Car = {Type : string; Age : int};; let cars = [{Type="Wagon"; Age=0};{Type="Lanver"; Age=10}];; cars |> List.filter (function |{Type = "Wagon"} -> true | _ -> false);; val it : Car list = [{Type = "Wagon"; Age = 0;}]
遅延評価
lazy()で評価対象を括っておけ。一回評価されるとキャッシュされた値が返る。
> let y = lazy( printfn "yを評価中・・・"; 30);; val y : Lazy<int> = 値は作成されていません。 > y.Value;; yを評価中・・・ val it : int = 30 > y.Value;; val it : int = 30