「48時間でSchemeを書こう/評価: 第二部」の版間の差分

削除された内容 追加された内容
en:Write Yourself a Scheme in 48 Hours/Evaluation, Part 2 (00:33, 26 March 2011 UTC) をインポート
 
最初のセクションを訳出
1 行
== 追加のプリミティブ: 部分適用 ==
== Additional Primitives: Partial Application ==
 
型エラーや悪い引数などを対処できるようになったので、プリミティブのリストを、単純な計算以上のことをするように肉付けしていきます。等価性評価、条件演算子、基本的な文字列操作などを加えましょう。
Now that we can deal with type errors, bad arguments, and so on, we'll flesh out our primitive list so that it does something more than calculate. We'll add boolean operators, conditionals, and some basic string operations.
 
始めに、以下をプリミティブのリストに加えてください。
Start by adding the following into the list of primitives:
 
("=", numBoolBinop (==)),
("<", numBoolBinop (<)),
(">", numBoolBinop (>)),
("/=", numBoolBinop (/=)),
(">=", numBoolBinop (>=)),
("<=", numBoolBinop (<=)),
("&&", boolBoolBinop (&&)),
("||", boolBoolBinop (||)),
("string=?", strBoolBinop (==)),
("string<?", strBoolBinop (<)),
("string>?", strBoolBinop (>)),
("string<=?", strBoolBinop (<=)),
("string>=?", strBoolBinop (>=)),
<syntaxhighlight lang="haskell">
("=", numBoolBinop (==)),
("&lt;<", numBoolBinop (&lt;<)),
("&gt;>", numBoolBinop (&gt;>)),
("/=", numBoolBinop (/=)),
("&gt;>=", numBoolBinop (&gt;>=)),
("&lt;<=", numBoolBinop (&lt;<=)),
("&amp;&amp;", boolBoolBinop (&amp;&amp;)),
("||", boolBoolBinop (||)),
("string=?", strBoolBinop (==)),
("string&lt;<?", strBoolBinop (&lt;<)),
("string&gt;>?", strBoolBinop (&gt;>)),
("string&lt;<=?", strBoolBinop (&lt;<=)),
("string&gt;>=?", strBoolBinop (&gt;>=)),
</syntaxhighlight>
 
これらは私たちがまだ書いていない補助関数、<code>numBoolBinop</code>・<code>boolBoolBinop</code>・<code>strBoolBinop</code>に依存しています。可変長の引数を取り整数を返す代わりに、これらは全て2つの引数を取り真偽値を返します。これらが互いに引数の型のみが違うので、一般化された<code>boolBinop</code>関数に重複箇所をまとめましょう。<code>boolBinop</code>はその引数に適用する<code>unpacker</code>によってパラメータ化されます。
These depend on helper functions that we haven't written yet: <span class="inline_code">numBoolBinop</span>, <span class="inline_code">boolBoolBinop</span> and <span class="inline_code">strBoolBinop</span>. Instead of taking a variable number of arguments and returning an integer, these both take exactly 2 arguments and return a boolean. They differ from each other only in the type of argument they expect, so let's factor the duplication into a generic <span class="inline_code">boolBinop</span> function that's parametrized by the unpacker function it applies to its arguments:
 
<syntaxhighlight lang="haskell">
boolBinop :: (LispVal -&gt;> ThrowsError a) -&gt;> (a -&gt;> a -&gt;> Bool) -&gt;> [LispVal] -&gt;> ThrowsError LispVal
boolBinop unpacker op args = if length args /= 2
then throwError $ NumArgs 2 args
else do left &lt;<- unpacker $ args !! 0
right &lt;<- unpacker $ args !! 1
return $ Bool $ left `op` right
</syntaxhighlight>
 
引数それぞれが型エラーを投げるかもしれないので、(<code>Error</code>モナドの)doブロックの中でそれらを順番にunpackしなくてはなりません。その後、<code>op</code>を2つの引数に適用して、それを<code>Bool</code>コンストラクタで包んで返します。どんな関数もバックティックで囲むことで中置演算子にすることができます(<code>`op`</code>)。
Because each arg may throw a type mismatch, we have to unpack them sequentially, in a do-block (for the Error monad). We then apply the operation to the two arguments and wrap the result in the Bool constructor. Any function can be turned into an infix operator by wrapping it in backticks (<span class="inline_code">`op`</span>).
 
型シグネチャも見てみてください。<code>boolBinop</code>は''2''つの関数を最初の二引数として取ります。一つ目の関数は<code>LispVal</code>からHaskellの地の型に戻すのに使われ、二つ目が実際に行うべき操作となります。振舞の違うところをパラメータ化することで、関数を再利用しやすくできます。
Also, take a look at the type signature. <span class="inline_code">boolBinop</span> takes ''two'' functions as its first two arguments: the first is used to unpack the arguments from LispVals to native Haskell types, and the second is the actual operation to perform. By parameterizing different parts of the behavior, you make the function more reusable.
 
では<code>boolBinop</code>を異なるunpackerで特定化する3つの関数を定義しましょう。
Now we define three functions that specialize boolBinop with different unpackers:
 
<syntaxhighlight lang="haskell">
numBoolBinop = boolBinop unpackNum
strBoolBinop = boolBinop unpackStr
boolBoolBinop = boolBinop unpackBool
</syntaxhighlight>
 
私たちはHaskellにどのように文字列を<code>LispVal</code>からunpackするかまだ教えていませんでした。これは値に対してパターンマッチを行ない、それを返すかエラーを投げる、という<code>unpackNum</code>と似た動作をします。繰り返しになりますが、文字列として解釈できる値を渡された時は、これらの関数は暗黙の内にそれを文字列に変換します。
We haven't told Haskell how to unpack strings from LispVals yet. This works similarly to unpackNum, pattern matching against the value and either returning it or throwing an error. Again, if passed a primitive value that could be interpreted as a string (such as a number or boolean), it will silently convert it to the string representation.
 
<syntaxhighlight lang="haskell">
unpackStr :: LispVal -&gt;> ThrowsError String
unpackStr (String s) = return s
unpackStr (Number s) = return $ show s
unpackStr (Bool s) = return $ show s
unpackStr notString = throwError $ TypeMismatch "string" notString
</syntaxhighlight>
 
真偽値をunpackするのにも似たようなコードを使います。
And we use similar code to unpack booleans:
 
<syntaxhighlight lang="haskell">
unpackBool :: LispVal -&gt;> ThrowsError Bool
unpackBool (Bool b) = return b
unpackBool notBool = throwError $ TypeMismatch "boolean" notBool
</syntaxhighlight>
 
次のステップに進む前に、これをコンパイル・テストして上手くいくことを確かめましょう。
Let's compile and test this to make sure it's working, before we proceed to the next feature:
 
<syntaxhighlight lang="text">
debian:/home/jdtang/haskell_tutorial/code#% ghc -package parsec -o simple_parser [../code/listing6.1.hs listing6.1.hs]<nowiki>
debian:/home/jdtang/haskell_tutorial/code#% ./simple_parser "(&lt;< 2 3)"
#t
debian:/home/jdtang/haskell_tutorial/code#% ./simple_parser "(&gt;> 2 3)"
#f
debian:/home/jdtang/haskell_tutorial/code#% ./simple_parser "(&gt;>= 3 3)"
#t
debian:/home/jdtang/haskell_tutorial/code#% ./simple_parser "(string=? \"test\" \"test\")"
#t
debian:/home/jdtang/haskell_tutorial/code#% ./simple_parser "(string&lt;<? \"abc\" \"bba\")"
#t
</syntaxhighlight>
</nowiki>
 
== Conditionals: Pattern Matching 2 ==
255 ⟶ 259行目:
#t
</nowiki>
 
練習問題
 
# Instead of treating any non-false value as true, change the definition of "if" so that the predicate accepts only Bool values and throws an error on any others.