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

削除された内容 追加された内容
102 行
</syntaxhighlight>
 
== List Primitivesリストのプリミティブ: car, cdr, and cons ==
 
おまけとして、リストを扱うプリミティブも加えましょう。Schemeのリストをペアによってではなく、Haskellの代数的データ型によって表現することにしたので、多くのLispに比べこれらプリミティブの定義はいくらか複雑になります。書き下されたS式に対してそれらがどう振る舞うかで考えるのが一番簡単でしょう。
For good measure, lets also add in the basic list-handling primitives. Because we've chosen to represent our lists as Haskell algebraic data types instead of pairs, these are somewhat more complicated than their definitions in many Lisps. It's easiest to think of them in terms of their effect on printed S-expressions:<span class="inline_lisp"></span>
 
# <code>(car '(a b c))</code> = a
# <code>(car '(a))</code> = a
# <code>(car '(a b . c))</code> = a
# <code>(car 'a)</code> = error エラー(not a listリストではない)
# <code>(car 'a 'b)</code> = error エラー(<code>car takes only one argument</code>は引数を一つしかとらない)
 
これらは極めて直感的にパターン節に変換することができます。<code>(x : xs)</code>がリストの最初の要素と残りを分けてくれることを思い出してください。
We can translate these fairly straightforwardly into pattern clauses, recalling that <span class="inline_code">(x : xs)</span> divides a list into the first element and the rest:
 
<syntaxhighlight lang="haskell">
car :: [LispVal] -&gt;> ThrowsError LispVal
car [List (x : xs)] = return x
car [DottedList (x : xs) _] = return x
car [badArg] = throwError $ TypeMismatch "pair" badArg
car badArgList = throwError $ NumArgs 1 badArgList
</syntaxhighlight>
 
<code>cdr</code>でも同じことをします。
Let's do the same with cdr:<span class="inline_lisp"></span>
 
# <code>(cdr '(a b c))</code> = (b c)
# <code>(cdr '(a b))</code> = (b)
# <code>(cdr '(a))</code> = NIL
# <code>(cdr '(a . b))</code> = b
# <code>(cdr '(a b . c))</code> = (b . c)
# <code>(cdr 'a)</code> = error エラー(not listリストではない)
# <code>(cdr 'a 'b)</code> = エラー(<code>car</code>は引数を一つしかとらない)
# (cdr 'a 'b) = error (too many args)
 
最初の3ケースは一つの節で表現することができます。私たちのパーサは<code>()</code>を<code>List []</code>として表現していて、<code>(x : xs)</code>というパターンを<code>[x]</code>に対してマッチさせる時、<code>xs</code>は<code>[]</code>に束縛されます。他の場合は別の節で扱いましょう。
We can represent the first 3 cases with a single clause. Our parser represents <span class="inline_lisp">'()</span> as <span class="inline_code">List []</span>, and when you pattern-match <span class="inline_code">(x : xs)</span> against <span class="inline_code">[x]</span>, xs is bound to []. The other ones translate to separate clauses:
 
<syntaxhighlight lang="haskell">
cdr :: [LispVal] -&gt;> ThrowsError LispVal
cdr [List (x : xs)] = return $ List xs
cdr [DottedList [xs] x] = return x
cdr [DottedList (_ : xs) x] = return $ DottedList xs x
cdr [badArg] = throwError $ TypeMismatch "pair" badArg
cdr badArgList = throwError $ NumArgs 1 badArgList
</syntaxhighlight>
 
<code>cons</code>はちょっと難しいので、節一つ一つを見ていきましょう。何かとnilコンスすると、nilを最後とする一要素のリストができます。
Cons is a little tricky, enough that we should go through each clause case-by-case. If you cons together anything with <span class="inline_lisp">Nil</span>, you end up with a one-item list, the <span class="inline_lisp">Nil</span> serving as a terminator:
 
<syntaxhighlight lang="haskell">
cons :: [LispVal] -&gt;> ThrowsError LispVal
cons [x1, List []] = return $ List [x1]
</syntaxhighlight>
 
何かとリストをコンスすると、リストの先頭に要素を貼り付けるような感じになります。
If you cons together anything and a list, it's like tacking that anything onto the front of the list:
 
<syntaxhighlight lang="haskell">
cons [x, List xs] = return $ List $ x : xs
</syntaxhighlight>
 
ただし、そのリストが<code>DottedList</code>だった場合、末尾は変らないのでそれは<code>DottedList</code>のままであるべきです。
However, if the list is a DottedList, then it should stay a DottedList, taking into account the improper tail:
 
<syntaxhighlight lang="haskell">
cons [x, DottedList xs xlast] = return $ DottedList (x : xs) xlast
</syntaxhighlight>
 
二つの非リストをコンスした場合、もしくはリストを先にした場合、''<code>DottedList</code>''ができます。これはそのようなコンスセルがnilで終わっていないからです。
If you cons together two non-lists, or put a list in front, you get a ''DottedList''. This is because such a cons cell isn't terminated by the normal Nil that most lists are.
 
<syntaxhighlight lang="haskell">
cons [x1, x2] = return $ DottedList [x1] x2
</syntaxhighlight>
 
最後に、2つより多いまたは少ない引数を渡されたらエラーです。
Finally, attempting to cons together more or less than 2 arguments is an error:
 
<syntaxhighlight lang="haskell">
cons badArgList = throwError $ NumArgs 2 badArgList
</syntaxhighlight>
 
Our last step is to implement 私たちの最後のステップは<code>eqv?. </code>を実装することです。Scheme offers 3 levels of equivalence predicatesは三段階の等価性述語を提供しています: [http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_sec_6.1 eq?, eqv?, and 、そしてequal?]. For our purposes, です。<code>eq? and </code>と<code>eqv? are basically the same: they recognize two items as the same if they print the same, and are fairly slow. So we can write one function for both of them and register it under </code>は私たちの目的からすると大体同じで、それらは2つのものが同じ字面を持てば同じであると認識し、かなり遅い。そこで私たちは一つの関数を書いて、<code>eq? and </code>と<code>eqv?.</code>という2つの名前で登録しましょう。
 
<syntaxhighlight lang="haskell">
eqv :: [LispVal] -&gt;> ThrowsError LispVal
eqv [(Bool arg1), (Bool arg2)] = return $ Bool $ arg1 == arg2
eqv [(Number arg1), (Number arg2)] = return $ Bool $ arg1 == arg2
eqv [(String arg1), (String arg2)] = return $ Bool $ arg1 == arg2
eqv [(Atom arg1), (Atom arg2)] = return $ Bool $ arg1 == arg2
eqv [(DottedList xs x), (DottedList ys y)] = eqv [List $ xs ++ [x], List $ ys ++ [y]]
eqv [(List arg1), (List arg2)] = return $ Bool $ (length arg1 == length arg2) &amp;&amp;
(all eqvPair $ zip arg1 arg2)
where eqvPair (x1, x2) = case eqv [x1, x2] of
Left err -&gt;> False
Right (Bool val) -&gt;> val
eqv [_, _] = return $ Bool False
eqv badArgList = throwError $ NumArgs 2 badArgList
</syntaxhighlight>
 
Most of these clauses are self-explanatory, the exception being the one for two Lists. This, after checking to make sure the lists are the same length, リストの比較以外、これらの殆どは自明です。これはリストが同じ長さかどうか確かめた後、2つのリストを[http://www.haskell.org/onlinereport/standard-prelude.html#$vzip zip]s the two lists of pairs, and then uses the function し、[http://www.haskell.org/onlinereport/standard-prelude.html#$vall all] to return false if を使ってどれか一つでも<code>eqvPair returns false on any of the pairs. </code>が偽を返すペアがあれば偽を返すようにします。<code>eqvPair is an example of a local definition: it is defined using the '</code>は局所的定義の一例です。それは<code>where' keyword, just like a normal function, but is available only within that particular clause of </code>を使って定義され、普通の関数のように働きますが、<code>eqv. Since we know that </code>のその節のその部分のみで有効です。<code>eqv only throws an error if the number of arguments is not </code>が引数の数が2, the line "である限りエラーを投げないので、<code>Left err -&gt;> False" will never be executed at the moment.</code>が実行されることはありません。
 
== Equal? and Weak Typing: Heterogenous Lists ==