「48時間でSchemeを書こう」の版間の差分

削除された内容 追加された内容
→‎戻り値: 一部訳出
→‎戻り値: 一部訳出
310 行
<code>$</code>演算子は中置関数適用です。<code>return (String x)</code>と書いても同じですが、<code>$</code>は右結合なので括弧を幾つか省くことができます。<code>$</code>は演算子なので、引数として他の関数に渡したり部分適用するなど、関数と同様に扱うことができます。この点に於て、<code>$</code>はLispの関数[http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-9.html#%_sec_6.4 apply]のように働きます。
 
Now let's move on to 次はScheme variables. An の変数です。[http://www.schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-5.html#%_sec_2.1 atom] is a letter or symbol, followed by any number of letters, digits, or symbols:は一つの文字か記号のあとに0個以上の文字、数字、または記号が連なったものです。
 
<syntaxhighlight lang="haskell">
parseAtom :: Parser LispVal
parseAtom =:: doParser first &lt;- letter &lt;|&gt; symbolLispVal
parseAtom = do first rest &lt;<- many (letter &lt;|&gt; digit &lt;<|&gt;> symbol)
rest let<- atommany =(letter first:rest<|> digit <|> symbol)
return $ caselet atom of= first:rest
return $ case atom "#t" -&gt; Bool Trueof
"#ft" -&gt;> Bool FalseTrue
_ "#f" -&gt;> AtomBool atomFalse
_ -> Atom atom
</syntaxhighlight>
 
ここでは新たに[http://www.cs.uu.nl/~daan/download/parsec/parsec.html#or &lt;|&gt;]演算子が登場しました。この演算子は一つ目のパーサを試し、それが失敗したら二つ目を試します。もしどちらかが成功すればそのパーサから返ってきた値を返します。最初のパーサは入力を消費する前に失敗しなければなりません。どのようにバックトラックを実装するかは後で見て行きます。
Here, we introduce another Parsec combinator, the choice operator [http://www.cs.uu.nl/~daan/download/parsec/parsec.html#or &lt;|&gt;]. This tries the first parser, then if it fails, tries the second. If either succeeds, then it returns the value returned by that parser. The first parser must fail before it consumes any input: we'll see later how to implement backtracking.
 
<code>let atom = first:rest</code>は新しい変数<code>atom</code>を定義します。一旦アトムの最初の文字と残りの文字列を読んだら、それらを一緒にしなければなりません。それにはリストのコンスオペレータ<code>:</code>を使います。<code>:</code>の代わりに<code>[first]++rest</code>の様に結合演算子<code>++</code>を使うこともできますが、<code>first</code>は一つの文字なので、角括弧で囲むことで一要素のリストにする必要があります。
Once we've read the first character and the rest of the atom, we need to put them together. The "let" statement defines a new variable <code>atom</code>. We use the list cons operator <code>:</code> for this. Instead of <code>:</code>, we could have used the concatenation operator <code>++</code> like this <code>[first]++rest</code>; recall that <code>first</code> is just a single character, so we convert it into a singleton list by putting brackets around it.
 
その後case文を使ってリテラルの真偽値とのマッチを試み、どの<code>LispVal</code>を作り返すか決定します。アンダースコア<code>_</code>は可読性向上のための仕掛けです。<code>_</code>はワイルドカードのようなものだと考えてください。caseブロックが<code>_</code>に辿りつくまでマッチに失敗し続けると、<code>_</code>は常にマッチするので、<code>atom</code>の値が返されます。
Then we use a case expression to determine which <code>LispVal</code> to create and return, matching against the literal strings for true and false. The underscore <code>_</code> alternative is a readability trick: case blocks continue until a <code>_</code> case (or fail any case which also causes the failure of the whole <code>case</code> expression), think of <code>_</code> as a ''wildcard''. So if the code falls through to the <code>_</code> case, it always matches, and returns the value of <code>atom</code>.
 
最後に、数字のパーサを作ります。これはモナドの値を取り扱うさらにもう一つのやり方を示しています。
Finally, we create one more parser, for numbers. This shows one more way of dealing with monadic values:
 
<syntaxhighlight lang="haskell">
parseNumber :: Parser LispVal
parseNumber :: Parser LispVal
parseNumber = liftM (Number . read) $ many1 digit
</syntaxhighlight>
 
関数適用(<code>$</code>)と関数合成(<code>.</code>)は両方とも右結合なので、後ろから読むと読みやすいです。Parsecのコンビネータ
It's easiest to read this backwards, since both function application (<code>$</code>) and function composition (<code>.</code>) associate to the right. The parsec combinator [http://www.cs.uu.nl/~daan/download/parsec/parsec.html#many1 many1] matches one or more of its argument, so here we're matching one or more digits. We'd like to construct a number <code>LispVal</code> from the resulting string, but we have a few type mismatches. First, we use the built-in function [http://www.haskell.org/onlinereport/standard-prelude.html#$vread read] to convert that string into a number. Then we pass the result to <code>Number</code> to get a <code>LispVal</code>. The function composition operator <code>.</code> creates a function that applies its right argument and then passes the result to the left argument, so we use that to combine the two function applications.
[http://www.cs.uu.nl/~daan/download/parsec/parsec.html#many1 many1]はその引数の一つ以上の連なりにマッチするので、ここでは一つ以上の数字にマッチすることになります。マッチ結果の文字列から<code>LispVal</code>の数値を作りたいのですが、型が合いません。まず、[http://www.haskell.org/onlinereport/standard-prelude.html#$vread read]で文字列を数字にし、その結果を<code>Number</code>に渡して<code>LispVal</code>を得ます。関数合成演算子<code>.</code>は右側の引数の関数を適用してその結果を左側の引数に渡して適用する関数を作るので、それを使って二つの関数適用を組み合せます。
 
Unfortunately, the result of 残念ながら、<code>many1 digit</code> is actually a の結果は<code>Parser String</code>, so our combined 型なので、<code>Number . read</code> still can't operate on it. We need a way to tell it to just operate on the value inside the monad, giving us back a をそれに直接作用させることはできません。何らかの方法でそれをモナドの中の値にのみ作用させ、<code>Parser LispVal</code>. The standard function を得なければいけませんが、それには標準関数の<code>liftM</code> does exactly that, so we apply というピッタリのものがあります。なので<code>liftM</code> to our <code>Number . read</code> function, and then apply the result of that to our parser.関数に適用し、それをパーサの結果に適用します。
 
<code>liftM</code>を使うにはプログラムの頭で<code>Monad</code>モジュールをインポートします。
We also have to import the Monad module up at the top of our program to get access to <code>liftM</code>:
 
<syntaxhighlight lang="haskell">
import Control.Monad
</syntaxhighlight>
 
This style of programming - relying heavily on function composition, function application, and passing functions to functions - is very common in Haskell code. It often lets you express very complicated algorithms in a single line, breaking down intermediate steps into other functions that can be combined in various ways. Unfortunately, it means that you often have to read Haskell code from right-to-left and keep careful track of the types. We'll be seeing many more examples throughout the rest of the tutorial, so hopefully you'll get pretty comfortable with it.