Haskellのパターンマッチングとは何ですか?保護された方程式とどのように関連していますか?
簡単な説明を探しましたが、見つかりませんでした。
編集:誰かが宿題としてタグ付けされました。私はもう学校に行きません、ただHaskellを学んでいて、そしてこの概念を理解しようとしています。純粋に興味がない。
一言で言えば、パターンは数学で区分的関数を定義するようなものです。パターンを使用して、引数ごとに異なる関数本体を指定できます。関数を呼び出すと、実際の引数をさまざまな引数パターンと比較することにより、適切な本体が選択されます。詳細は Haskellの穏やかな紹介 を参照してください。
比較:
同等のHaskell:
fib 0 = 1
fib 1 = 1
fib n | n >= 2
= fib (n-1) + fib (n-2)
ピースワイズ関数の "n≥2"がHaskellバージョンのガードになりますが、他の2つの条件は単なるパターンです。パターンは、x:xs
、(x, y, z)
、Just x
など、値と構造をテストする条件です。区分的定義では、=
または∈
関係に基づく条件(基本的に、何かが「何か」であると言う条件)がパターンになります。ガードは、より一般的な条件を考慮に入れています。 fib
を書き換えてガードを使用することができます。
fib n | n == 0 = 1
| n == 1 = 1
| n >= 2 = fib (n-1) + fib (n-2)
他にも良い答えがあるので、私はあなたに非常に技術的な答えを与えるつもりです。パターンマッチングは、代数的データ型の除去構成です:
「消去構造」とは、「値を消費または使用する方法」を意味します
「代数的データ型」は、ファーストクラスの関数に加えて、Clean、F#、Haskell、MLなどの静的に型付けされた関数型言語の大きなアイデアです
代数的データ型の考え方は、物事のタイプを定義し、その物事を作成する方法をすべて言うことです。例として、「文字列のシーケンス」を3つの方法で代数的データ型として定義してみましょう。
data StringSeq = Empty -- the empty sequence
| Cat StringSeq StringSeq -- two sequences in succession
| Single String -- a sequence holding a single element
さて、この定義にはあらゆる種類の問題がありますが、例として、任意の長さのシーケンスの一定時間の連結を提供するので興味深いです。 (これを達成する他の方法があります。)宣言は、Empty
、Cat
、およびSingle
を導入します。これらは、シーケンスの作成。 (これにより、それぞれがintroduction構成になり、物を作る方法になります。)
Cat
でシーケンスを作成するには、他に2つのシーケンスが必要です。Single
でシーケンスを作成するには、要素(この場合は文字列)が必要ですここに重要なポイントがあります。除去構造、パターンマッチングは、シーケンスを精査して質問する方法を提供しますどのコンストラクターで作成しましたか?。どんな答えでも準備する必要があるので、コンストラクタごとに少なくとも1つの代替を提供します。これが長さ関数です:
slen :: StringSeq -> Int
slen s = case s of Empty -> 0
Cat s s' -> slen s + slen s'
Single _ -> 1
言語の中核では、すべてのパターンマッチングはこのcase
構成に基づいて構築されています。ただし、代数的データ型とパターンマッチングは言語のイディオムにとって非常に重要であるため、関数定義の宣言形式でパターンマッチングを行うための特別な「構文糖」があります。
slen Empty = 0
slen (Cat s s') = slen s + slen s'
slen (Single _) = 1
この構文シュガーを使用すると、パターンマッチングによる計算は、方程式による定義によく似ています。 (Haskell委員会は意図的にこれを行いました。)そして、他の回答でわかるように、case
式でガードを叩くことによって、方程式または代替のいずれかを特殊化することが可能です。シーケンスの例のもっともらしいガードは考えられません。他の回答にも例がたくさんあるので、ここには残しておきます。
パターンマッチングは、少なくともHaskellでは、 代数的データ型 の概念に深く結びついています。次のようなデータ型を宣言すると、
data SomeData = Foo Int Int
| Bar String
| Baz
...Foo
、Bar
、およびBaz
をコンストラクタ-として定義しますOOPの「コンストラクター」と混同しないでください。他の値からSomeData
値を作成します。
パターンマッチングはこれを逆に行う以外に何もありません-パターンはSomeData
値を構成要素(実際、パターンマッチングはHaskellで値を抽出するonly方法だと思います。
型に複数のコンストラクターがある場合、パターンごとに関数の複数のバージョンを記述し、使用されたコンストラクターに応じて正しいバージョンが選択されるようにします(可能なすべての構造に一致するようにパターンを記述したと仮定します-これは一般的には良いことです)行うための練習)。
パターンマッチングは、手続き型プログラミングのバックグラウンドから来た場合、頭を動かすのが難しい、これらの痛みを伴う操作の1つです。データ構造の作成に使用したものと同じ構文を使用してマッチングを行うことができるため、理解するのは難しいと思います。
F#では、cons演算子::
を使用して、次のようにリストの先頭に要素を追加できます。
let a = 1 :: [2;3]
//val a : int list = [1; 2; 3]
同様に、同じ演算子を使用して、次のようにリストを分割できます。
let a = [1;2;3];;
match a with
| [a;b] -> printfn "List contains 2 elements" //will match a list with 2 elements
| a::tail -> printfn "Head is %d" a //will match a list with 2 or more elements
| [] -> printfn "List is empty" //will match an empty list