私はHaskellを始めたばかりですが、私が見つけたすべてのオンラインチュートリアルから、条件付き制御ステートメントを実行するための1つの受け入れられた方法があるかどうかを見つけることができないようです。if-else、ガード、パターンマッチングを見たことがありますが、それらはすべて同じことを達成しているようです。他の方法よりも一般的に受け入れられている/より速い/より効率的な方法はありますか?
4 に答える
他の方法よりも一般的に受け入れられている/より速い/より効率的な方法はありますか?
ガードは、パターンマッチングに続くif-then-elseの(かなり複雑な)構文糖衣です。If-then-elseは、case
overのシンタックスシュガーですBool
。したがって、これらのことはほとんど同じように効率的です。
しかし、ここに観察があります:パターンマッチで効率的であるブール式で非効率的に行うのはしばしば簡単です。Haskellプログラマーを始めたときのお気に入りの例は、次のように書くことです。
length xs == 0
の長さに比例するコストxs
、ここで
case xs of { [] -> True; _:_ -> False }
一定の時間がかかります。
何が起こっているかをより正確に観察する方法は、(ビューパターンのような派手な拡張機能がない場合)、パターンマッチの最悪の場合のコストは、左側に表示されるコンストラクターの数に比例するということです。高価で小さいパターンマッチ。対照的に、ブール式のサイズは、それを評価するのにかかる費用については何も教えてくれません。この意味で、そしてこの意味でのみ、パターンマッチングはif-then-elseやガードよりも安価です。
初心者にとって良いヒューリスティックは、可能な限りパターンマッチングを使用することです。あなたがより多くの経験を積むにつれて、あなたはあなたのアプローチを洗練することができます。
そうですね、Haskellで「コントロールステートメント」の観点から考えることが最善の方法かどうかはわかりません。とは言うものの、ほとんどすべては最終的にパターンマッチングに帰着します。if ... then ... else
たとえば、のコンストラクターでのパターンマッチングの観点から定義できるようなブール条件Bool
。
最も「原始的な」形式はおそらくステートメントです。関数定義のパターンマッチングは、1つの大きな式case
を含む単一の関数定義に対する単なる構文糖衣です。case
何を使うべきかという点では、概念的に最も意味のあるものを選んでください。パターンマッチングは、代数的データ型を分解する必要がある場合に最も適しています。if
ブロックは、いくつかの述語に対して単純なyes/noの結果が必要な場合に適しています。ガードは通常、データ型の分解とブール述語を混在させる必要がある場合に使用されます。
覚えておくべき最も重要な点は、パターンマッチングが代数的データ型を分解する唯一の方法であるということです。ブール述語は高階関数に簡単に置き換えることができますが、データコンストラクター内で値を抽出するにはパターンマッチングが必要です。
3 つのオプションのいずれも、まったく同じことを行うものではなく、すべての状況で使用できるものではありません。
パターン マッチは、指定された値を作成するために使用されたコンストラクターをチェックし、変数をバインドします。ifもguardsもそうしません。式を実装する型の nullary コンストラクター (または数値リテラル) に対して一致する場合にのみ、パターン一致の代わりにそれらを使用できます。
例:
foo (Just x) = x+1 -- Can not do this without a pattern match (except by using
-- functions like fromJust that themselves use pattern matches)
foo Nothing = 0 -- You could do this using a pattern guards like
-- foo x | x==Nothing = 0, but that is less readable and less
-- concise than using a plain pattern match
パターン ガードを使用すると、等しいかどうかをチェックできます。たとえば、指定された数値がゼロより大きいかどうかを確認できます。もちろん、if でも同じことができますが、パターン ガードを使用すると、ガードが失敗したときに次のパターンに進むことができます。これにより、if を使用するよりも繰り返しを減らすことができます。例:
maybeSqrt (Just x) | x >= 0 = sqrt x
maybeSqrt _ = Nothing
if を使用すると、次のようになります (Nothing の繰り返しに注意してください)。
maybeSqrt (Just x) = if x >= 0 then sqrt x
else Nothing
maybeSqrt _ = Nothing
最後に if はパターン マッチなしで使用できます。与えられた値に対して実際にパターン マッチングを使用しない場合は、case x of ...
単にパターン ガードを使用できるようにするだけでは意味がほとんどなく、単に if を使用するよりも読みやすく簡潔ではありません。
コードの見栄えを良くし、読みやすくするものに基づいて選択します。@Don が指摘するように、これらのさまざまな形式の多くは にコンパイルされcase
ます。利用可能なシンタックス シュガーのため、見た目が異なります。このシュガーはコンパイラ用ではなく、人間用です。したがって、他の人が読みたいと思うものと、あなたにとって読みやすいものに基づいて決定してください。