web-dev-qa-db-ja.com

Haskellパターンマッチング-それは何ですか?

Haskellのパターンマッチングとは何ですか?保護された方程式とどのように関連していますか?

簡単な説明を探しましたが、見つかりませんでした。

編集:誰かが宿題としてタグ付けされました。私はもう学校に行きません、ただHaskellを学んでいて、そしてこの概念を理解しようとしています。純粋に興味がない。

41
Tony The Lion

一言で言えば、パターンは数学で区分的関数を定義するようなものです。パターンを使用して、引数ごとに異なる関数本体を指定できます。関数を呼び出すと、実際の引数をさまざまな引数パターンと比較することにより、適切な本体が選択されます。詳細は Haskellの穏やかな紹介 を参照してください。

比較:

Fibonacci sequence

同等の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)
64
outis

他にも良い答えがあるので、私はあなたに非常に技術的な答えを与えるつもりです。パターンマッチングは、代数的データ型除去構成です:

  • 「消去構造」とは、「値を消費または使用する方法」を意味します

  • 「代数的データ型」は、ファーストクラスの関数に加えて、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

さて、この定義にはあらゆる種類の問題がありますが、例として、任意の長さのシーケンスの一定時間の連結を提供するので興味深いです。 (これを達成する他の方法があります。)宣言は、EmptyCat、および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式でガードを叩くことによって、方程式または代替のいずれかを特殊化することが可能です。シーケンスの例のもっともらしいガードは考えられません。他の回答にも例がたくさんあるので、ここには残しておきます。

25
Norman Ramsey

パターンマッチングは、少なくともHaskellでは、 代数的データ型 の概念に深く結びついています。次のようなデータ型を宣言すると、

data SomeData = Foo Int Int
              | Bar String
              | Baz

...FooBar、およびBazコンストラクタ-として定義しますOOPの「コンストラクター」と混同しないでください。他の値からSomeData値を作成します。

パターンマッチングはこれを逆に行う以外に何もありません-パターンはSomeData値を構成要素(実際、パターンマッチングはHaskellで値を抽出するonly方法だと思います。

型に複数のコンストラクターがある場合、パターンごとに関数の複数のバージョンを記述し、使用されたコンストラクターに応じて正しいバージョンが選択されるようにします(可能なすべての構造に一致するようにパターンを記述したと仮定します-これは一般的には良いことです)行うための練習)。

12
C. A. McCann

パターンマッチングは、手続き型プログラミングのバックグラウンドから来た場合、頭を動かすのが難しい、これらの痛みを伴う操作の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
2
Igor Zevaka