実は根本的に違うんです!いずれにせよ、少なくとも Haskell では。
ガードはよりシンプルで柔軟です。本質的には一連の if/then 式に変換される特別な構文です。ガードに任意のブール式を入れることができますが、通常の ではできないことは何もしませんif
。
パターン マッチは、さらにいくつかのことを行います。データを分解する唯一の方法であり、スコープ内で識別子をバインドします。ガードがif
式と同等であるのと同じ意味で、パターン マッチングはcase
式と同等です。宣言 (最上位またはlet
式のようなもの) もパターン一致の形式であり、「通常の」定義は単純なパターン (単一の識別子) と一致します。
また、Haskell で実際に行われる主な方法は、パターン マッチである傾向があります。パターン内のデータを分解しようとすることは、評価を強制する数少ないことの 1 つです。
ところで、トップレベルの宣言で実際にパターン マッチングを行うことができます。
square = (^2)
(one:four:nine:_) = map square [1..]
これは、関連する定義のグループに役立つ場合があります。
GHCは、両方を組み合わせた ViewPatterns 拡張機能も提供します。バインディング コンテキストで任意の関数を使用し、結果に対してパターン マッチを行うことができます。もちろん、これはまだ通常のものの構文糖衣にすぎません。
どちらをどこで使用するかという日常の問題については、大まかなガイドを次に示します。
1 つまたは 2 つのコンストラクターの深さで直接一致できるものには、必ずパターン マッチングを使用してください。複合データ全体はあまり気にしませんが、構造の大部分は気にします。この@
構文を使用すると、全体的な構造を変数にバインドしながらパターン マッチングも行うことができますが、1 つのパターンで多くのことを行うと、すぐに見苦しく、読みにくくなります。
Int
2 つの値を比較してどちらが大きいかを確認するなど、パターンにうまく対応していないプロパティに基づいて選択する必要がある場合は、必ずガードを使用してください。
大規模な構造の奥深くからいくつかのデータのみが必要な場合、特に構造全体を使用する必要がある場合、ガードとアクセサー関数は通常、@
とでいっぱいの巨大なパターンよりも読みやすくなり_
ます。
異なるパターンで表される値に対して同じことを行う必要があるが、それらを分類するための便利な述語を使用する必要がある場合は、通常、ガード付きの単一のジェネリック パターンを使用する方が読みやすくなります。一連のガードが網羅的でない場合、すべてのガードに失敗したものはすべて、次のパターン (存在する場合) にドロップされることに注意してください。したがって、一般的なパターンをフィルターと組み合わせて例外的なケースをキャッチし、それ以外のすべてに対してパターン マッチングを実行して、関心のある詳細を取得できます。
パターンで簡単にチェックできるものには絶対にガードを使用しないでください。空のリストのチェックは典型的な例です。そのためにはパターン マッチを使用します。
一般に、疑わしい場合は、デフォルトでパターン マッチングをそのまま使用してください。通常は、その方が適切です。パターンが本当に見苦しくなったり、複雑になったりしたら、それを書く方法を考えるのをやめてください。ガードを使用する以外に、部分式を個別の関数として抽出したりcase
、関数本体内に式を配置して、パターン マッチングの一部をメインの定義から除外したりすることもできます。