はじめに
これは予想される、またはそうではない継続です。 今日、私たちは誇らしげにFP(機能的プログラミング)を具体化した青い錠剤を飲み込み、F#の機能的な部分をさらに深く掘り下げます。 関数、再帰、パターンマッチング、その他いくつかの興味深いことについて話しましょう。 面白い? その後、錠剤を飲み込み、ダイビングを開始します。
浸漬
まず、FPの起源、λ計算についてお話したいと思います。 結局、関数型プログラミングの基礎を築いたのは、アロンゾ教会によって作成されたこの数学的理論でした。 数学について長く退屈な講義をするのではなく、簡単に説明します。 最終的には、F#だけでなく、その数学的な背景においても、知識を持っている人を驚かせることができます。 実際、λ計算のこのひどい名前は何ですか? その教会は数学を一般化し、絶対的にすべてが基本概念の一つである「機能」の形で表されるようにしたかったのです。 さらに、彼の理論は、すべてが機能であることを示唆しています。 「問題は何ですか? 既存の機能の概念を使用しないのはなぜですか?」読者は尋ねます。 実際、数学者はどこでもどこでも機能を使用したかったので、多くの場合、学校/研究所で教えられていたように、名前を与えることに抵抗があります:
f(x) = x - 5
f(6) = 6 - 5 = 1
次に、Alonzoは、名前を付けずに関数を記述できる表記法を考案しました。
λx.x-5
この場合、関数は名前を持たないことを除いて、前の例とまったく同じです。 彼女は同じ方法で引数の値を渡すことができます:
(λx.x - 5) 6 = 6 - 5 = 1
また、λ関数のパラメーターで、別のλ関数を渡すことができます。 前の記事で出会ったラムダ関数との直接的な類似性ははっきりと見えると思います。
「ナンセンスな話をやめて! より多くのコード。
そして、私は数学でほとんど終わりました。 ほぼ。 結局のところ、誰もが再帰とは何かを知っていますか? いいね フィボナッチ数を計算するための再帰関数(それ自体を呼び出す)を作成します。 フィボナッチ数-一連の数。ここで、後続の各数は前の数の合計に等しくなります。
Fib = {0, 1, 1, 2, 3, 5, 8, ...}
これを関数の言語で記述すると、フィボナッチ数をシーケンスの数で返す関数は次のようになります。
f(n) = f(n - 1) + f(n - 2)
f(1) = 1
f(2) = 1
そして、F#では次のようになります。
let rec fib n = match n with | 1 | 2 -> 1 | n -> fib(n-1) + fib(n-2)
ここで考慮すべき2つの新しいキーワードがあります。
- rec-関数が再帰的であることを示します
- matchは、パターンマッチングメカニズムです。C#またはC / C ++に精通している人は、パターンマッチングをステロイドのスイッチとして表すことができます。
関数内で何が起こりますか? 実際、すべてが非常に単純です。引数nが1または2の場合、1を返します。それ以外の場合、シーケンス内の前の2つの数値の合計を返します。 実際には、matchははるかに多くの可能性を提供しますが、少し後でそれに戻り、最初にF#の主要なタイプを検討します。
型宣言
型を宣言するには、奇妙なことにtypeキーワードを使用する必要があります。 これまでのところ、賢明なものは宣言できないため、intを再定義してください。
type myInt = int
タプル
タプル、またはタプルは、F#のある種の複合型です。 何をどのようにすばやく理解するために、例を検討してください。
// int*int; * - let point2d = (10, 5) // int*int*int let point3d = (1, 1, 2) // string*int let personAndId = ("Some Name", 3) let x2d = fst point2d let y2d = snd point2d
この例からわかるように、タプルは2つ以上のタイプの組み合わせです(名前ではなく、順序付けされています)。 point2dおよびpoint3dは、2次元および3次元平面上のポイントを表します。personAndId-名前とID。 タプルを操作するために、F#標準ライブラリには2つの関数があります。fstとsndで、それぞれタプルの最初と2番目の要素を返します。 3番目のアイテムを取得する方法は?
// ; _ , / let third (_, _, t) = t
タプル型宣言の例を次に示します。
// -; string, int type personAndId = string*int // personAndId; : , let person:personAndId = ("Habr", 1)
レコード別名レコード
もちろん、これは記録に関するものではありません。 レコードはF#の別の構造単位です。 タプルとは異なります。レコードの各フィールドには名前がありますが、内容は任意です。 構文を考慮してください:
type person = {name : string; surname : string; id : int} let sampleUser = {name = "Habra"; surname = "Habr"; id = 1} let sampleUserName = sampleUser.name
説明は必要ないと思います。
差別化された組合、またはタグ付き組合
次の構造単位は関連付けです。 さまざまな方法でデータを組み合わせることができます。 簡単な結合の例を次に示します。
type Currency = | Liter of float | Pint of float let liter = Liter 5.0 let pint = Pint 10.0
パターンマッチング、またはパターンマッチング
読者はすでにF#の基本構造単位について多くのことを知っているので、パターンマッチングについてさらに詳しく説明できます。 一致は、命令型言語とオブジェクト指向言語からの切り替えに多少似ていますが、より広い可能性を提供するだけです。 唯一の制限は、テンプレートが可能な値の全範囲をカバーする必要があるということです。 私はそれが最初は不快で不合理に思えることに注意したい。
// , let isOdd n = (n % 2 = 1) // , 1 3 let oneToThree n = match n with | 1 -> printfn "One" | 2 -> printfn "Two" | 3 -> printfn "Three" // ; _ n | n -> printfn "Geather than three" // type Point = int * int // matching' let findZero point = match point with | (0, 0) -> printfn "x = 0 and y = 0" | (0, y) -> printfn "(0, %d)" y | (x, 0) -> printfn "(%d, 0)" x | _ -> printfn "x <> 0 and y <> 0" type Person = | NameOnly of string | IdOnly of int // - | IdAndName of string * int // matching' let personInfo person = match person with | NameOnly(name) -> printf "Name is %s" name | IdOnly(id) -> printf "Id is %d" id | IdAndName(name, id) -> printf "Name is %s and Id is %d?" name id // matching'a let matchWithCondition x = match x with | x when x >= 5. && x <= 10. -> printfn "x is between 5.0 and 10.0" | _ -> ()
より完全な説明はここにあります 。
ジェネリック型
型、つまり文字列のツリー、intのツリーなどを持つバイナリツリーの実装が必要だとします。 F#の型は一般化できます。これは次の方法で行います。
// ' type 'a BinaryTree = // , - | Node of 'a BinaryTree * 'a BinaryTree | Value of 'a (* : type BinaryTree<'a> = | Node of BinaryTree<'a> * BinaryTree<'a> | Value of 'a *) // , , let tree1 = Node( Node (Value 1, Value 2), Node (Value 5, Value 100) ) let tree2 = Node( Node (Value "Binary tree", Value "in F#"), Node (Value "is so", Value "Simple") )
はい、F#の一般化されたバイナリツリーはそのように見えます。
おわりに
まあ私は何を言うことができます、私は無駄にしようとしなかったことを望み、多くはこの投稿も好きになるでしょう。 次回は、青い錠剤を飲み込み、リストとシーケンス、カリー化機能などについて話し続けます。 読んでくれてありがとう。
何を読む
まず、 msdnで 、今日話されたことの多くの詳細な説明を見つけることができます。
第二に、実世界のための関数型プログラミング(Tomas Petricek、Jon Skeet)は、問題を解決するための命令型アプローチに慣れている人のための本です。 この投稿のいくつかの例は、そこから部分的に取られました。
少ない人のために
フィボナッチ
実際、上記の投稿で提供されたコードはひどいものです。 しかし、それは理解しやすいです。 そして、より機能的なアプローチを見たい人のために、より有能な例を挙げます(ありがとう、 onikiychuka ):
let fib = Seq.unfold (fun state ->Some(fst state + snd state, (snd state, fst state + snd state))) (1,1) fib |> Seq.take 20
前述のように、次回はそのような機能を詳細に分析します。
パターンマッチング
型式
let second (x, y) = y let getValueFromOption (Some value) = value
パターンマッチングメカニズムも表します。 jack128とonikiychukaに感謝します。