プログラミング Haskell 第 2 版 †
プログラミングHaskell 第2版 Grahum Hutton ラムダノート 2019-08-02 ¥ 3,456 |
第4章 関数定義 †
p.40
4.2 条件式 †
多くのプログラミング言語とは違って、Haskell の条件式には常に else部が必要です。
ぶらさがり else 問題 †
これにより、いわゆる「ぶらさがり else 問題」が回避できます。
もし else部が省略可能な文であるなら、文「if True then if False then 1 else 2」が 2を返すかエラーとなるかは、else部が内側と外側のどちらの条件に対応すると解釈するかに依存してしまうでしょう。
- ぶら下がりelse問題とは【Dangling else problem】 | MaryCore https://marycore.jp/coding/dangling-else-problem/
ぶら下がりelse(Dangling else)は、特定のif文に紐付くよう意図されていたelse文が、書き手の意図に反して異なる別のif文に紐付いてしまっている状態のことを言う。
この状態によって引き起こされる問題は宙ぶらりんelse問題(dangling else problem)とも呼ばれている。1 2 3 4 5 6 7 8 9 10 11
if (!a) if (b) b(); else // ← ぶら下がりのelse a(); /* プログラム側ではこのように解釈されてしまう */ if (!a) if (b) b(); else a();
ぶら下がりelseの問題は、フリーフォーマットを採用し、かつブレース(波括弧 = {})の省略が可能なC言語やJava, PHP, Perl, JavaScript等の言語で頻繁に発生する。
オフサイドルールを採用して字下げのレベルに意味を持たせたPythonやCofeeScript等の言語ではこの問題は起こらない。
またブレースの記述を必須とするSwiftやGo, Rust等の言語も同様にこの問題は起こらない。
Haskellの場合は、ifはelseを必須にすることによって、曖昧さを排除しているんですね!
4.3 ガード付きの等式 †
ガード付きの等式は、条件式と比べて、条件が多くなったときに読みやすい点が優れています。
たとえば、プレリュード関数 signumは以下のように定義すると理解しやすくなります。
- 条件式
1 2 3
signum :: Int -> Int signum n = if n < 0 then -1 else if n == 0 then 0 else 1
- ガード付きの等式
1 2 3
signum n | n < 0 = -1 | n == 0 = 0 | otherwise = 1
p.41
4.4 パターンマッチ †
多くの関数は、パターンマッチを使うことで簡潔かつ直観的に定義できます。
パターンマッチによる関数定義では、パターンと呼ばれる式に基づいて、列挙された同じ型の候補の中から結果が選ばれます。
もし最初のパターンに合致したら最初の結果、そうではなく二つめのパターンに合致したら二つめの結果、という具合に結果が選ばれます。たとえば、真理値の否定を返す否定演算子 notは、パターンマッチを使って以下のように定義できます。
1 2 3not :: Bool -> Bool not False = True not True = False
ワイルドカード †
すべての値に合致するワイルドカード「_」
p.42
4.4.2 リスト・パターン †
cons演算子 †
これまでは Haskell のリストを分解不可能なデータとみなしてきました。
実際には、リストは合成されたデータであり、空リスト []に対して演算子:を使って要素を一つずつ増やしていくことで生成されます。
「:」は既存のリストの先頭に新しい要素を追加して新しいリストを生成する演算子で、「作成する」(construct)という意味でcons演算子と呼ばれます。
たとえば、リスト[1,2,3]は以下のように分解できます。
[1,2,3] 1 : [2,3] 1 : (2 : [3]) 1 : (2 : (3 : []))
すなわち、[1,2,3]は 1 : (2 : (3 : []))の略記法にすぎません。
リストを扱うときに括弧が増えないよう、cons 演算子は右結合になっています。
たとえば、1 : 2 : 3 : []は 1 : (2 : (3 : []))という意味です。
p.43
cons 演算子はリストの作成だけでなくパターンの作成にも利用できます。