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

削除された内容 追加された内容
評価器作成の手始め・練習問題の訳出
62 行
== 評価器作成の手始め: プリミティブ ==
 
では評価器を作りましょう。評価器の目標はコードの型を、評価結果であるデータの型に写すことです。Lispでは、コードとデータ両方の型が同じなので、評価器はLispValを返します。他の言語ではコードが様々な文法によるより複雑な構造を持っていることが多いです。
Now, we start with the beginnings of an evaluator. The purpose of an evaluator is to map some "code" data type into some "data" data type, the result of the evaluation. In Lisp, the data ''types'' for both code and data are the same, so our evaluator will return a LispVal. Other languages often have more complicated code structures, with a variety of syntactic forms.
 
数値・文字列・真偽値・クォートされた文字列を評価するのは非常に簡単で、それ自身を返せばよいだけです。
Evaluating numbers, strings, booleans, and quoted lists is fairly simple: return the datum itself.
 
<syntaxhighlight lang="haskell">
eval :: LispVal -&gt; LispVal
eval val@(String:: _)LispVal =-> valLispVal
eval val@(NumberString _) = val
eval val@(BoolNumber _) = val
eval val@(Bool _) = val
eval (List [Atom "quote", val]) = val
</syntaxhighlight>
 
新しい種類のパターンが出てきました。この記法<code>val@(String _)</code>は文字列である<code>LispVal</code>全てにマッチし、<code>val</code>にその<code>LispVal</code>''全体''(<code>String</code>コンストラクタの中身だけでなく)を束縛します。戻り値は<code>String</code>型ではなく<code>LispVal</code>型を持ちます。アンダースコアは「どうでもいい」変数で、どんな値にもマッチしますが、どんな値にも束縛されません。アンダースコアはどんなパターンにおいても使うことができますが、主に@パターンとコンストラクタタグを調べたいだけの時に便利です。
This introduces a new type of pattern. The notation <span class="inline_code">val@(String _)</span> matches against any LispVal that's a string and then binds <span class="inline_code">val</span> to the ''whole'' LispVal, and not just the contents of the String constructor. The result has type LispVal instead of type String. The underbar is the "don't care" variable, matching any value yet not binding it to a variable. It can be used in any pattern, but is most useful with @-patterns (where you bind the variable to the whole pattern) and with simple constructor-tests where you're just interested in the tag of the constructor.
 
最後の節はネストしたパターンの例です。<code>List</code>に含まれるデータの型は<code>LispVal</code>のリスト<code>[LispVal]</code>です。ここでは''それ''を取って<code>[Atom "quote", val]</code>という二つの値、シンボル"quote"と<code>val</code>に束縛される任意の値を含むリストに対してマッチさせ、<code>val</code>を返します。
The last clause is our first introduction to nested patterns. The type of data contained by <span class="inline_code">List</span> is <span class="inline_code">[LispVal]</span>, a list of LispVals. We match ''that'' against the specific two-element list <span class="inline_code">[Atom "quote", val]</span>, a list where the first element is the symbol "quote" and the second element can be anything. Then we return that second element.
 
では<code>eval</code>を既にあるコードに統合しましょう。まず<code>readExpr</code>が式の文字列表現の代わりに式そのものを返すように直しましょう。
Let's integrate <span class="inline_code">eval</span> into our existing code. Start by changing readExpr back so it returns the expression instead of a string representation of the expression:
 
<syntaxhighlight lang="haskell">
readExpr :: String -&gt;> <span class="changed_code">LispVal</span>
readExpr input = case parse parseExpr "lisp" input of
Left err -&gt;> <span class="changed_code">String $ </span>"No match: " ++ show err
Right val -&gt;> <span class="changed_code">val</span>
</syntaxhighlight>
 
そして<code>main</code>関数を、式を読み、それを評価して、文字列に変えた後、表示するように変更します。今や私たちは<code>>>=</code>演算子と関数合成演算子を知っているので、それらを使ってコードを少し簡潔にしましょう。
And then change our main function to read an expression, evaluate it, convert it to a string, and print it out. Now that we know about the &gt;&gt;= monad sequencing operator and the function composition operator, let's use them to make this a bit more concise:
 
<syntaxhighlight lang="haskell">
main :: IO ()
<span class="changed_code">
main = getArgs &gt;&gt;>>= print . eval . readExpr . head
main :: IO ()
</syntaxhighlight>
main = getArgs &gt;&gt;= print . eval . readExpr . head
</span>
 
<code>main</code>では、<code>getArgs</code>アクションの結果を取り、以下のように働く合成関数に渡します。
Here, we take the result of the getArgs action (a list of strings) and pass it into the composition of:
 
# 最初の値を取り(<code>head</code>)、
# Take the first value (<span class="inline_code">head</span>)
# Parse it 構文解析して(<span class="inline_code"code>readExpr</spancode>)
# Evaluate it 評価し(<span class="inline_code"code>eval</spancode>)
# 文字列に変換して表示します(<code>print</code>)。
# Convert it to a string and print it (<span class="inline_code">print</span>)
 
いつも通りコンパイル・実行してください。
Compile and run the code the normal way:
 
<syntaxhighlight lang="text">
debian:/home/jdtang/haskell_tutorial/code#% ghc -package parsec -o eval listing4.2.hs
debian:/home/jdtang/haskell_tutorial/code#% ./eval "'atom"
atom
% ./eval 2
debian:/home/jdtang/haskell_tutorial/code# ./eval 2
2
debian:/home/jdtang/haskell_tutorial/code#% ./eval "\"a string\""
"a string"
debian:/home/jdtang/haskell_tutorial/code#% ./eval "(+ 2 2)"
Fail: listing6.hs:83: Non-exhaustive patterns in function eval
 
Fail: listing6.hs:83: Non-exhaustive patterns in function eval
We still can't do all that much useful with the program (witness the failed (+ 2 2) call), but the basic skeleton is in place. Soon, we'll be extending it with some functions to make it useful.
</syntaxhighlight>
 
今のところ特に実用的なことは出来ませんが(<code>(+ 2 2)</code>が失敗していますね)、基本的な骨格は出来上がりました。さらに関数を足してこれを拡張していきましょう。
 
== 基本的なプリミティブを加える ==