アットウィキロゴ

Haskell > 真偽値

等しいかとその他の比較

(前回の復習) 前回の章で、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つしか存在し得ない。それは、TrueFalseである。(Haskellでは先頭は大文字)

ちなみにブール値という用語は数学者、哲学者であるジョージ・ブールにちなんで名付けられた。

型の導入

TrueFalseが値であるという時、ただ単に類似をしているわけではない。Haskellではブール値は数値と同じ状態を持っていて、実際、数字とちょうど同じように 操作することができる。些細な例を挙げるとしたら、真偽値が等しいかの比較であろう:

Prelude> True == True
True
Prelude> True == False
False

確かにTrueTrueに等しく、TrueFalseに等しくない。では、2Trueに等しいかすぐに答えられるか?

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)は数でなく、 それ故、等しいかの比較は吹き飛んだのである。

故に、一般的な概念は値はを持つということであり、これらの型がその値で何が出来て、何が出来ないのかを定義する。例えば今回の場合、TrueBool型の値であり、 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.

Infix operators

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:

Template:HaskellGHCi

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:

Template:HaskellGHCi

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:

Template:HaskellGHCi

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.

Boolean operations

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:

  • (&&) performs the and operation. Given two boolean values, it evaluates to True if both the first and the second are True, and to False otherwise.

Template:HaskellGHCi

  • (||) performs the or operation. Given two boolean values, it evaluates to True if either the first or the second are True (or if both are true), and to False otherwise.

{{HaskellGHCi|1= Prelude> (2 + 2 == 5) Template:!! (2 > 0) True Prelude> (Template:!!) (18 == 17) (9 >= 11) False }}

  • not performs the negation of a boolean value; that is, it converts True to False and vice-versa.

Template:HaskellGHCi

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).

Guards

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>

Template:HaskellExample

Remarkably, the above code is about as readable as the corresponding mathematical definition. Let us dissect the components of the definition:

  • We start just like a normal function definition, providing a name for the function, abs, and saying it will take a single parameter, which we will name x.
  • Instead of just following with the = and the right-hand side of the definition, we entered a line break, and, following it, the two alternatives, placed in separate lines.We could have joined the lines and written everything in a single line, but in this case it would be a lot less readable. These alternatives are the guards proper. Note that the whitespace is not just for aesthetic reasons; it is necessary for the code to be parsed correctly.
  • Each of the guards begins with a pipe character, |. After the pipe, we put an expression which evaluates to a boolean (also called a boolean condition or a predicate), which is followed by the rest of the definition – the equals sign and the right-hand side which should be used if the predicate evaluates to True.
  • The otherwise case is used when none of the preceding predicates evaluate to True. In this case, if x is not smaller than zero, it must be greater than or equal to zero, so the final predicate could have just as easily been x >= 0; but otherwise works just as well.

Template:body note

Template:body note

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.

Template:Haskell/NotesSection


Template:Haskell navigation

Template:Auto category

最終更新:2014年09月24日 20:41