パターンマッチングとスイッチの説明
-
11-07-2019 - |
質問
私は、switchステートメントとパターンマッチング(F#)の違いを2、3人に説明しようとしていましたが、実際にはうまく説明できませんでした。 "では、なぜif..then..else"を使用しないのですか。
どのように説明しますか?
編集!すばらしい回答をしてくれたすべての人に感謝します。複数の正解をマークできたらと思います。
解決
以前は「これらの人々」の1人でしたが、なぜパターンマッチングがこんなにおいしいのかを簡潔にまとめる簡単な方法があることはわかりません。体験的です。
パターンマッチングをひと目見ただけで、それが栄光に満ちたswitchステートメントであると思ったとき、代数データ型(タプルと判別ユニオン)を使ったプログラミングの経験はなく、そのパターンはまったく見なかったと思いますマッチングは、制御構造 結合構造の両方でした。 F#でプログラミングを行ったので、ようやく「取得」しました。パターンマッチングの優れた点は、関数型プログラミング言語に見られる機能の合流によるものです。したがって、部外者が内部を見るのは簡単ではありません。
言語とAPI設計に関する2回の短いブログシリーズの第2回でパターンマッチングが役立つ理由の1つの側面を要約しようとしました。 パートおよびパート2 。
他のヒント
パターンは、照合する値の構造を記述するための小さな言語を提供します。構造は任意に深くすることができ、変数を構造化された値の一部にバインドできます。
これにより、非常に簡潔に記述できます。これを、簡単なタイプの数式の導関数などの小さな例を使用して説明できます。
type expr =
| Int of int
| Var of string
| Add of expr * expr
| Mul of expr * expr;;
let rec d(f, x) =
match f with
| Var y when x=y -> Int 1
| Int _ | Var _ -> Int 0
| Add(f, g) -> Add(d(f, x), d(g, x))
| Mul(f, g) -> Add(Mul(f, d(g, x)), Mul(g, d(f, x)));;
さらに、パターンマッチングは静的型の静的な構造であるため、コンパイラは(i)すべてのケースをカバーしたことを検証できます(ii)値に決して一致しない冗長ブランチを検出します(iii)非常に効率的な実装を提供します(ジャンプなど)。
このブログ記事からの抜粋:
パターンマッチングには、switchステートメントやメソッドディスパッチよりもいくつかの利点があります:
- パターンの一致はintに基づいて機能しますが、 フロート、文字列、およびその他のタイプ オブジェクトと同様。
- パターンの一致は、いくつかの 同時に異なる値: 並列パターンマッチング。方法 ディスパッチと切り替えは単一に制限されます 値、例" this"。
- パターンをネストして、 任意の木の上のディスパッチ 深さ。メソッドのディスパッチと切り替えは制限されています ネストされていない場合。
- Orパターンでは、サブパターンを 共有しました。メソッドのディスパッチのみ許可 メソッドがからのときの共有 たまたまベースを共有するクラス クラス。それ以外の場合は手動で行う必要があります 共通性を考慮して 別の機能(それを与える 名前)そして、手動で呼び出しを挿入します すべての適切な場所から 不要な機能。
- パターンマッチングは冗長性を提供します エラーをキャッチするものをチェックします。
- ネストされたパターンおよび/または並列パターン マッチは F#コンパイラ。同等のオブジェクト指向は 手書きで絶えず書く 中に手で再最適化 開発、これは法外に 退屈でエラーが発生しやすい 生産品質のオブジェクト指向コードは 比較して非常に遅くなります。
- アクティブパターンを使用すると、注入することができます カスタムディスパッチセマンティクス。
頭の上から:
- コンパイラは、マッチのすべての可能性をカバーしていないかどうかを判断できます
- 割り当てとして一致を使用できます
- 差別化された組合がある場合、各試合に異なる「タイプ」を設定できます
スイッチは2つの前輪です。
パターンマッチングは車全体です。
タプルには"、"がありますバリアントにはCtor引数があります。これらはコンストラクタであり、物を作成します。
パターンはデストラクタであり、それらを引き裂きます。
これらは二重の概念です。
これをより強制的に言うと、タプルまたはバリアントの概念は、そのコンストラクタだけでは説明できません。デストラクタが必要であるか、作成した値が無用です。値を定義するのはこれらの二重の記述です。
一般に、コンストラクタはデータ、デストラクタは制御フローと考えています。バリアントデストラクタは代替ブランチ(多くの1つ)であり、タプルデストラクタは並列スレッド(多くのすべて)です。
並列性は次のような操作で明らかです
(f * g) . (h * k) = (f . h * g . k)
関数を介して制御が流れることを考える場合、タプルは計算を制御の並列スレッドに分割する方法を提供します。
このように見れば、式はタプルとバリアントを構成して複雑なデータ構造を作成する方法です(ASTのようなものです)。
また、パターンマッチはデストラクタを構成する方法です(もう一度、ASTを考えてください)。
OCamlのパターン一致は、上記のいくつかの方法で述べたように表現力が高いことに加えて、いくつかの非常に重要な静的な保証を提供します。コンパイラーは、パターン一致ステートメントによって具体化されるケース分析が以下であることを証明します。
- 網羅的(見逃されるケースはありません)
- 非冗長(前のケースに横取りされるためヒットすることのないケースはありません)
- サウンド(問題のデータ型を与えられた場合に不可能なパターンはありません)
これは本当に大したことです。初めてプログラムを作成するときに役立ち、プログラムが進化しているときに非常に役立ちます。マッチステートメントを適切に使用すると、コード内の型を確実に変更しやすくなります。これは、型システムが壊れたマッチステートメントを指摘するためです。
If-Else(またはswitch)ステートメントは、手元の値のプロパティに応じて、値(入力)を処理するさまざまな方法を選択することに関するものです。
パターンマッチングは、その構造に基づいて値を処理する方法を定義することです(単一のケースパターンマッチが意味があることに注意してください)。
したがって、パターンマッチングは選択よりも値の分解に関するものです。これにより、誘導構造(再帰ユニオン型)で(再帰)関数を定義するための非常に便利なメカニズムになります。 。
PS:pattern-matchとIf-Else" patterns"を知っているかもしれません。数学でのアドホックな使用から。
" xにプロパティAがある場合、y else z" (If-Else)
" p1..pnの一部の用語。ここで....はx ..の素数分解です。 ((単一ケース)パターンマッチ)
おそらく、文字列と正規表現で類推できますか?探しているものを何記述し、コンパイラーに方法を見つけさせます。これにより、コードがはるかに単純かつ明確になります。
余談ですが、パターンマッチングで最も役立つのは、良い習慣を身に付けることです。コーナーケースを最初に処理します 、すべてのケースをカバーしたことを確認するのは簡単です。