(前回の復習) 前回の章で、Haskellで等号を変数や関数を定義するのに使う方法を見てきた。 例えば、以下のコード
r = 5
で定義のスコープに沿って置き換わって意味の通じるところ全てで5に置き換わる rが現れる。また、
f x = x + 3
で数字が1つ後に続き(fの引数として取る)、その数に3を足すのに置き換わる fが現れる。 (復習おわり)
しかし、数学では、等号は微妙に違っていて、同様に重要な意味でも使われる。 例えば、この問題について考える:
|
例題:次の方程式を解きなさい: x+3=5 |
このような問題を見たとき、5がx+3の代わりになるとか、 その逆にすぐに関心を持つわけでない。代わりに、方程式x+3=5を 命題、と理解し、ある数xが3足すと結果として5となるという意味となる。 「方程式を解く」とは命題が真となるようなxの値を、存在するのならば、 見つけ出すということである。この例題では、初等代数学を用いれば、等式をx=5-3,x=2と変形し、目的の解を求めることが出来る。 また、解が等式を成立させることは元の等式にx=2を代入することで 確認できる。例題では2+3=5という明らかに真である結果になる。 (注:最新版が壊れていたのでここの部分に関しては旧版から持ってきた)
等しいかどうかを見るために値を比較することはプログラミングにおいても便利である。 Haskellではそのような比較をちょうど方程式のように見える自然な方法で書くことが出来る。 等号は既にものを定義するのに使われているので、代わりに等号2つ (==)を使う。実際の動きを見るには、GHCiを起動し上で書いた命題を以下のように入力すれば良い:
Prelude> 2 + 3 == 5 True
2 + 3は5に等しいためGHCiは"True"(真)と返す。では、真でない等式 を用いたらどうなるだろうか:
Prelude> 7 + 3 == 5 False
快適で繋がりのある結果になった。次にこのような比較の中に自分で定義した関数を使ってみる。復習の部分で取り上げた関数fを使ってみる:
Prelude> let f x = x + 3 Prelude> f 2 == 5 True
f 2はちょうど2 + 3なので、予想通りの結果である。
等しいかを確かめるのに加えて、同じほど容易に2つの数値のどちらが大きいかを見るために比較することもできる。Haskellでは<(小なり)、>(大なり)、<=(以下)、>=(以上)といった多くの比較が利用でき、==(等しい)と ちょうど同様に機能する。簡単な応用例として、前回の章の円の面積を求める関数areaのそばに<を使って、ある半径の円の面積がある値より小さいか を調べることが出来る。
Prelude> let area r = pi * r^2 Prelude> area 5 < 50 False
今までの説明でGHCiである算術的命題が真か偽かを知ることができることを学んだ。 そのことはそれでよいのだが、これがどのようにプログラムを書くときに便利なの だろうか?そして、GHCiがそのような"質問"に対して"答える"とき実際は何が起こっている のだうか?何が起こっているのかを理解するために、一旦別の関連することについて考える。 GHCiに計算式を入力した時、式は評価され、結果となる数値が画面上に表示される:
Prelude> 2 + 2 4
ここで計算式を等しいかの比較に変えると、似たようなことが起こる:
Prelude> 2 == 2 True
表示されるTrueが何なのか?確実に数のようには見えない。命題2 == 2 が正しいかを示す何かと思うことが出来る。この視点から、Trueを値―総計や数量等の類を表すものではないが―としてみなすのは意味が通っている。ある命題が正しいかどうか を表している。このような値のことを真偽値やブール値という。 当然なことだが、ブール値は2つしか存在し得ない。それは、Trueと Falseである。(Haskellでは先頭は大文字)
ちなみにブール値という用語は数学者、哲学者であるジョージ・ブールにちなんで名付けられた。
TrueやFalseが値であるという時、ただ単に類似をしているわけではない。Haskellではブール値は数値と同じ状態を持っていて、実際、数字とちょうど同じように 操作することができる。些細な例を挙げるとしたら、真偽値が等しいかの比較であろう:
Prelude> True == True True Prelude> True == False False
確かにTrueはTrueに等しく、TrueはFalseに等しくない。では、2がTrueに等しいかすぐに答えられるか?
Prelude> 2 == True
<interactive>:1:0:
No instance for (Num Bool)
arising from the literal `2' at <interactive>:1:0
Possible fix: add an instance declaration for (Num Bool)
In the first argument of `(==)', namely `2'
In the expression: 2 == True
In the definition of `it': it = 2 == True
正しい答えは比較できないである、なぜならこの質問は単に意味を成さないからである。 数字を数字でないものを比較することは出来ないし、ブール値とブール値でないものを比較することは出来ない。Haskellは上の式を取り入れ、出てきた醜いエラーメッセージは本質的に、 ちょうどこのことを言っている。分かりづらいごちゃごちゃをすべて無視すれば(最終的には 理解できるようになる)、上のメッセージは==の左辺に数(Num)があり、 それ故、数の類が右辺にあると思われた。しかし、ブール値(Bool)は数でなく、 それ故、等しいかの比較は吹き飛んだのである。
故に、一般的な概念は値は型を持つということであり、これらの型がその値で何が出来て、何が出来ないのかを定義する。例えば今回の場合、TrueはBool型の値であり、 The correct answer is you can't, because the question just does not make sense. It is impossible to compare a number with something that is not a number, or a boolean with something that is not a boolean. Haskell incorporates that notion, and the ugly error message we got is, in essence, stating exactly that. Ignoring all of the obfuscating clutter (which we will get to understand eventually), that message says that there was a number (Num) on the left side of the ==, and so some kind of number was expected on the right side; however, a boolean value (Bool) is not a number, and so the equality test exploded into flames.
Thus, the general concept is that values have types, and these types define what we can or cannot do with the values. In this case, for instance, True is a value of type Bool, as is False (as for the 2, while there is a well-defined concept of number in Haskell the situation is slightly more complicated, so we will defer the explanation for a little while). Types are a very powerful tool because they provide a way to regulate the behaviour of values with rules which make sense, making it easier to write programs that work correctly. We will come back to the topic of types many times as they are very important to Haskell.
What we have seen so far leads us to the conclusion that an equality test like 2 == 2 is an expression just like 2 + 2, and that it also evaluates to a value in pretty much the same way. That fact is actually given a passing mention on the ugly error message we got on the previous example:
Therefore, when we type 2 == 2 in the prompt and GHCi "answers" True it is just evaluating an expression. But there is a deeper truth involved in this process. A hint is provided by the very same error message:
GHCi called 2 the first argument of (==). In the previous module, we used argument to describe the values we feed a function with so that it evaluates to a result. It turns out that == is just a function, which takes two arguments, namely the left side and the right side of the equality test. The only special thing about it is the syntax: Haskell allows two-argument functions with names composed only of non-alphanumeric characters to be used as infix operators, that is, placed between their arguments. The only caveat is that if you wish to use such a function in the "standard" way (writing the function name before the arguments, as a prefix operator) the function name must be enclosed in parentheses. So the following expressions are completely equivalent:
This makes it clear how (==) is a function with two arguments just like areaRect from the previous module. What's more, the same considerations apply to the other relational operators we mentioned (<, >, <=, >=) and to the arithmetical operators (+, *, etc.) – all of them are just functions. This generality is an illustration of one of the strengths of Haskell: it is a language with very few "special cases", which helps to keep things simple. In general, we can say that all tangible things in Haskell are either values or functions.In case you found this statement bold, know that we will go even further in due course.
To see both truth values and infix operators in action, let's consider the boolean operations which manipulate truth values as in logic propositions. Haskell provides us three basic functions for that purpose:
{{HaskellGHCi|1= Prelude> (2 + 2 == 5) Template:!! (2 > 0) True Prelude> (Template:!!) (18 == 17) (9 >= 11) False }}
Another relational operator is not equal to. It is already provided by Haskell as the (/=) function, but if we were to implement it ourselves, a natural way would be:
<source lang = "haskell"> x /= y = not (x == y) </source>
Note that it is perfectly legal syntax to write operators infix, even when defining them. Completely new operators can also be created out of ASCII symbols (which means mostly the common symbols used on a keyboard).
Now we have explored what is really happening with boolean operators, but we've done little more than testing one-line expressions here. We still don't know how this can be used to make real programs. We will tackle this issue by introducing guards, a feature that relies on boolean values and allows us to write more interesting functions.
Let us implement the absolute value function. The absolute value of a number is the number with its sign discardedTechnically, that just covers how to get the absolute value of a real number, but let's ignore this detail for now.; so if the number is negative (that is, smaller than zero) the sign is inverted; otherwise it remains unchanged. We could write the definition as:
|x| = \begin{cases} x, & \mbox{if } x \ge 0 \\ -x, & \mbox{if } x < 0. \end{cases}
Here, the actual expression to be used for calculating |x| depends on a set of propositions made about x. If x \ge 0 is true, then we use the first expression, but if x < 0 is the case, then we use the second expression instead. We need a way to express this decision process in Haskell. Using guards, the implementation could look like this:abs is also provided by Haskell, so in a real-world situation you don't need to worry about providing an implementation yourself.</tt>
Remarkably, the above code is about as readable as the corresponding mathematical definition. Let us dissect the components of the definition:
where and Guards where clauses are particularly handy when used with guards. For instance, consider a function which computes the number of (real) solutions for a quadratic equation, ax^2 + bx + c = 0:
<source lang = "haskell"> numOfSolutions a b c
| disc > 0 = 2
| disc == 0 = 1
| otherwise = 0
where
disc = b^2 - 4*a*c
</source>
The where definition is within the scope of all of the guards, sparing us from repeating the expression for disc.