F#はMicrosoftのDon Syme氏が開発したプログラミング言語で、命令型、オブジェクト指向、関数型の要素を併せ持つマルチパラダイム言語と位置付けられています。実装はオープンソースのMITライセンスのもと、githubのリポジトリで公開されています。
実行環境は.NET Frameworkおよび.NET CoreやMonoといったマルチプラットフォームに対応していますが、サポート状況については各プラットフォームごとの情報を参照してください(「F# の概要」など)。
なお、ここで対象とするF#のバージョンは4.1(FSharp.Core 4.4.1.0)とします。
letは定義するもの
F#のletは変数や関数を定義するのに使われます。どこに出てくるかによって役割が変わることがあります。また、let
に追加するキーワードによっても定義されるものの使われ方が変わります。
変数に値を設定するとき、そのあとに値を変更できるようにするにはlet mutable
としなければなりません。値を変更するときは=
ではなく<-
を使います。
(* 変数の定義 *)
let a = 1 // 変数に値を設定(値の変更不可)
let mutable b = 2 // 値の変更可
b <- 3 // 値を変更
関数の定義でlet mutable
を使うときは、関数をfun args -> ...
のように定義しなくてはなりません。
(* 関数の定義 *)
let f a b = a + b // 関数の内容を変更できない
f 2 3 // 5
let mutable g = fun a b -> a + b // 変数の内容を変更できる
g 2 3 // 5
g <- fun a b -> a * b
g 2 3 // 6
また、繰り返し処理で使われる再帰関数(自分自身を呼び出す関数)を定義するときはlet rec
で定義します。
let rec len n = match n with h :: t -> 1 + len t | [] -> 0 // リストの長さ(要素の数)を数える
len [5; 3; 7] // 3
変数や関数の型を指定するときは、その名称に:型名
を続けます。
let n: int = 3
let f (a: int) (b: int) = a + b
let g: int -> int = fun x -> x * 2
let
では複数の変数それぞれに値を定義できます。また、タプルの各要素の値を別々の変数に設定できます。
let a, b, c = 3, 1, 2 // a = 3, b = 1, c = 2
let a, b, c = (3, 1, 2) // a = 3, b = 1, c = 2
letの中にブロック
letは変数や関数を定義するものですが、その中でより複雑な処理の内容を定義するために、2行目以降にインデントをつけて処理のまとまりを表すブロックを定義できます。インデントの深さ(行の書き始めの位置)が同じなら同じ層のブロックとなります。
let rec sum n =
(* 2行目以降にインデントをつけるとブロックになる *)
match n with
| h :: t -> h + sum t
| [] -> 0
sum [5; 3; 7] // 15 (各要素の合計)
letの中にlet
ブロック中には別のlet
も定義できます。ブロック内で定義された変数や関数はその中のみで使えます。また、ブロック内で最後に得られた結果がそのブロックの処理結果となります。
(* リストの各要素の平均値を計算する関数 *)
let avg n =
let rec len n = match n with h :: t -> 1 + len t | [] -> 0
let rec sum n = match n with h :: t -> h + sum t | [] -> 0
sum n / len n // ブロックの最終結果 → この関数の処理結果
avg [5; 3; 7] // 15
(* avg内のlenやsumはavgの外では実行できない *)
// len [5; 3; 7]
// sum [5; 3; 7]
letでは終われない
こうして複数行にわたって書かれるブロックはlet
で終われないことになっています。そのため、ブロックはlet
の次にそれ以外の処理を実行してから終わるようにします。
let f a b =
let x = a
let y = b
() // letでは終われない
for i in {1..5} do
let a = i
() // letでは終われない
type C(x: int) =
do
let a = x
() // letでは終われない
#if INTERACTIVE
#nowarn "25"
#endif
try
let a = None
let (Some x) = a // パターンが一致しない(withブロックへ)
() // letでは終われない
with
ex -> eprintfn "%s" ex.Message // "一致条件が不完全でした"
アクティブパターン
アクティブパターンは判定の結果を表す識別子を定義するもので、すべての結果に識別子をつけるものと、結果の一部のみに識別子を定義するパーシャルアクティブパターンがあります。
アクティブパターンはlet
の次に(|識別子|...|)
で識別子を定義し、右辺で識別子を割り振る処理を定義します。
let (|Odd|Even|) n = if n % 2 = 0 then Even else Odd // アクティブパターンの定義
let a = 1
match a with Odd -> "odd" | Even -> "even" // "odd"
パーシャルアクティブパターンはlet
の次の識別子の定義を(|識別子|_)
とし、処理の内容をSome 値
かNone
のどちらかが割り振られるように定義します。
let (|Fizz|_|) n = if n % 3 = 0 then Some "Fizz" else None
let (|Buzz|_|) n = if n % 5 = 0 then Some "Buzz" else None
let fizzbuzz n =
match n with
| Fizz s ->
match n with
| Buzz t -> s + " " + t // "Fizz Buzz"
| _ -> s // "Fizz"
| _ ->
match n with
| Buzz s -> s // "Buzz"
| _ -> string n // それ以外の数
[1..15] |> List.map fizzbuzz
// ["1"; "2"; "Fizz"; "4"; "Buzz"; "Fizz"; "7"; "8"; "Fizz"; "Buzz"; "11"; "Fizz"; "13"; "14"; "Fizz Buzz"]
列挙型の識別子と値の変換
let
とenum
を使うと、列挙型の識別子と、それに定義づけられた値とを変換できます。
(* 列挙体の定義 *)
type En =
| A = 1
| B = 2
(* 列挙体の値を変数に設定(以下のletはいずれも等価) *)
let ea = En.A // 識別子を指定
let ea = enum<En> 1 // 値とジェネリックを指定
let ea: En = enum 1 // 変数に型を指定
ea = En.A // true
判別共用体の値を取り出す
let
には判別共用体の値を取り出す機能もありますが、パターンに当てはまらない場合があるときは「この式のパターン マッチが不完全です」という警告が出ることがあります。
type DU = A of int
let (A a) = A 1 // a = 1
type DU = A of int * string
let (A (a, b)) = A (1, "uno") // a = 1, b = "uno"
type DU = A of int | B of string
let (A a) = A 1 // 警告「この式のパターン マッチが不完全です」(Aしか当てはまらない)
let (B b) = B "uno" // 警告「この式のパターン マッチが不完全です」(Bしか当てはまらない)
mutableにはレコード型にも
mutable
はlet
だけでなくレコード型にも定義できます。これにより、レコード型の値も後から変更できるようになります。
type R = {mutable a: int; b: string} // aを変更できるレコード型
let r = {a = 1; b = "uno"} // レコード型の定義
r.a <- 2 // {a = 2; b = "uno"} // aの値を変更
測定単位と型
測定単位を使うと、数値に付随する単位を加味した計算ができます。測定単位は[<Measure>] type 単位
のように定義し、単位を持つ数値は数値<単位>
のように表します。こうした単位が付随する数値の型名は数値型<単位>
となります。単位は、たとえば1<m>
を1<s>
で割ると1<m/s>
というように計算の結果が逐次反映されます。
数値に単位をつけるには1<単位>
を掛けます。逆に数値から単位をはずすにはint
やfloat
などでキャスト(型変換)します。
(* 測定単位の設定 *)
[<Measure>] type m
[<Measure>] type cm
[<Measure>] type s
(* 単位を使った計算 *)
let v = 6.75<m> / 1.5<s> // 4.5<m/s>
let a = v / 1.5<s> // 3.0<m/s^2>
(* 単位を変換する関数 *)
let m2Cm (n: float<m>) = n * 100.0<cm/m>
let cm2M (n: float<cm>) = n / 100.0<cm/m>
(* 関数の実行例 *)
m2Cm 1.5<m> // 150.0 : float<cm>
cm2M 150.0<cm> // 1.5 : float<m>
(* 単位の付加 *)
let setM n = n * 1.0<m>
setM 1.5 // 1.5<m>
(* 単位の除去 *)
let rejM (n: float<m>) = float n
rejM 1.5<m> // 1.5
module内のlet
モジュール内でlet
により定義された変数や関数は、モジュールの外からでも、モジュールにアクセス可能であれば利用できます。しかしモジュール内でlet private
により定義されたものはモジュールの外からアクセスできません。
module MA =
let a = 1
let private pa = 3
module MB =
let b = 2
let private pb = 4
MA.a = 1 // true
// MA.pa = 3 にはアクセスできない
MB.b = 2 // true
// MB.pb = 4 にはアクセスできない
(* コメント行で示したようにはアクセスできない *)
module MA =
module MB =
let b = 2
let private pb = 4
let a = MB.b
// let private pa = MB.pb
let c = MA.a
let d = MA.MB.b
// let pd = MA.MB.pb
クラス内のlet
プライマリコンストラクタを持つクラスでは、インスタンスを生成するごとにtype
内にあるlet
が実行されます。これは抽象クラスと具象クラスのどちらでも行われます。このlet
ではコンストラクタ変数を使った処理も実行できます。let
で定義された変数や関数はインスタンスのメンバーもアクセスできます。ただし、これらに対しインスタンスの外部からはアクセスできません。
(* プライマリコンストラクタのあるクラスではインスタンス生成時にletが実行される *)
(* 抽象クラス *)
[<AbstractClass>]
type AC(x: int, y: int) =
let a, b = x, y
let f a b = a + b
do
printfn "%i + %i = %i" a b <| f a b
abstract member F: int with get
(* 具象クラス(ACを継承) *)
type C(x, y) as self =
inherit AC(x, y)
let a, b = (x + 1), (y + 1)
let f a b = a * b
do
printfn "%i * %i = %i" a b <| self.F
override this.F = f a b
let c = C(2, 3) // "2 + 3 = 5", "3 * 4 = 12"の表示(doブロックの実行)
c.F // 12
// c.a, c.b, c.fにはアクセスできない
ここをご覧いただいた方々の参考になればと思います。