「48時間でSchemeを書こう/練習問題の解答」の版間の差分

削除された内容 追加された内容
M編集の要約なし
167 行
 
<li>
<syntaxhighlight lang="haskell">
data LispVal = Atom String
| List [LispVal]
184 ⟶ 185行目:
"newline" -> '\n'
otherwise -> (value !! 0)
</syntaxhighlight>
 
The combination of anyChar and notFollowedBy ensure that only a single character is read.
189 ⟶ 191行目:
Note that this does not actually conform to the standard; as it stands, "space" and "newline" must be entirely lowercase; the standard states that they should be case insensitive.
 
<syntaxhighlight lang="haskell">
 
parseExpr :: Parser LispVal
parseExpr = parseAtom
196 ⟶ 198行目:
<|> try parseBool -- these can all start with the hash char
<|> try parseCharacter
</syntaxhighlight>
</li>
 
<li>
A possible solution for floating point numbers:
<syntaxhighlight lang="haskell">
 
parseFloat :: Parser LispVal
parseFloat = do x <- many1 digit
206 ⟶ 209行目:
y <- many1 digit
return $ Float (fst.head$readFloat (x++"."++y))
</syntaxhightlight>
Furthermore, add
<syntaxhighlight lang="haskell">
try parseFloat
</syntaxhighlight>
''before'' parseNumber in parseExpr and the line
<syntaxhighlight lang="haskell">
| Float Double
</syntaxhighlight>
to the LispVal type.
</li>
215 ⟶ 223行目:
<li>
'''Ratio''', using Haskell's Ratio type:
<syntaxhighlight lang="haskell">
 
parseRatio :: Parser LispVal
parseRatio = do x <- many1 digit
221 ⟶ 229行目:
y <- many1 digit
return $ Ratio ((read x) % (read y))
</syntaxhighlight>
 
Additionally, import the ''Ratio'' module, add
<syntaxhighlight lang="haskell">
try parseRatio
</syntaxhighlight>
''before'' parseNumber in parseExpr and the line
<syntaxhighlight lang="haskell">
| Ratio Rational
</syntaxhighlight>
to the LispVal type.
 
231 ⟶ 243行目:
 
'''Complex''' using Haskell's Complex type:
<syntaxhighlight lang="haskell">
toDouble :: LispVal -> Double
toDouble(Float f) = f
241 ⟶ 254行目:
char 'i'
return $ Complex (toDouble x :+ toDouble y)
</syntaxhighlight>
 
As before, import the ''Complex'' module, add
<syntaxhighlight lang="haskell">
try parseComplex
</syntaxhighlight>
''before'' parseNumber and parseFloat in parseExpr and the line
<syntaxhighlight lang="haskell">
| Complex (Complex Double)
</syntaxhighlight>
to the LispVal type.
</li>
254 ⟶ 271行目:
===Exercise 1===
These two are analogous to parseQuoted:
<syntaxhighlight lang="haskell">
parseQuasiQuoted :: Parser LispVal
parseQuasiQuoted = do
265 ⟶ 283行目:
x <- parseExpr
return $ List [Atom "unquote", x]
</syntaxhighlight>
 
Also add
<syntaxhighlight lang="haskell">
<|> parseQuasiQuoted
<|> parseUnQuote
</syntaxhighlight>
to parseExpr.
 
===Exercise 2===
I chose to go with Arrays as described in [http://www.haskell.org/ghc/docs/latest/html/libraries/array-0.3.0.0/Data-Array.html Data.Array] and used list-array conversions for array construction.
<syntaxhighlight lang="haskell">
parseVector :: Parser LispVal
parseVector = do arrayValues <- sepBy parseExpr spaces
283 ⟶ 304行目:
char ')'
return x)
</syntaxhighlight>
 
===Exercise 3===
 
This took a fair amount of fiddling with <code>sepBy</code>, <code>endBy</code> and friends. I started by getting the <code>(. degenerate)</code> dotted list to work and then went from there. This code tolerates trailing and leading spaces.
<syntaxhighlight lang="haskell">
 
parseAnyList :: Parser LispVal
parseAnyList = do
299 ⟶ 320行目:
(Nil ()) -> List head
otherwise -> DottedList head tail
</syntaxhighlight>
 
 
Alternative solution without a Nil constructor. <code>spaces</code> is the spaces from Parsec and <code>spaces1</code> is the spaces from this tutorial.
<syntaxhighlight lang="haskell">
 
parseList :: Parser LispVal
parseList = do char '(' >> spaces
311 ⟶ 332行目:
return $ DottedList head tail
<|> (spaces >> char ')' >> (return $ List head))
</syntaxhighlight>
 
=Chapter 3=
 
===Exercise 1===
Here is one way of adding a few of them.
<syntaxhighlight lang="haskell">
 
primitives :: [(String , [LispVal] -> LispVal)]
primitives = [("+" , numericBinop (+)) ,
346 ⟶ 367行目:
listp (DottedList _ _) = Bool True
listp _ = Bool False
</syntaxhighlight>
 
===Exercise 2===
<syntaxhighlight lang="haskell">
 
unpackNum :: LispVal -> Integer
unpackNum (Number n) = n
unpackNum _ = 0
</syntaxhighlight>
 
===Exercise 3===
 
Add symbol->string and string->symbol to the list of primitives, then:
<syntaxhighlight lang="haskell">
 
symbol2string, string2symbol :: LispVal -> LispVal
symbol2string (Atom s) = String s
362 ⟶ 383行目:
string2symbol (String s) = Atom s
string2symbol _ = Atom ""
</syntaxhighlight>
 
This doesn't deal well with bad input, which is covered later.
 
368 ⟶ 389行目:
 
===Exercise 1===
<syntaxhighlight lang="haskell">
 
eval env (List [Atom "if", pred, conseq, alt]) = do
result <- eval env pred
375 ⟶ 396行目:
Bool True -> eval env conseq
_ -> throwError $ TypeMismatch "bool" pred
</syntaxhighlight>
 
===Exercise 2===
Define a helper function that takes the equal/eqv function as an argument:
<syntaxhighlight lang="haskell">
 
eqvList :: ([LispVal] -> ThrowsError LispVal) -> [LispVal] -> ThrowsError LispVal
eqvList eqvFunc [(List arg1), (List arg2)] = return $ Bool $ (length arg1 == length arg2) &&
385 ⟶ 406行目:
Left err -> False
Right (Bool val) -> val
</syntaxhighlight>
 
Now adjust the eqv clause:
<syntaxhighlight lang="haskell">
 
eqv [l1@(List arg1), l2@(List arg2)] = eqvList eqv [l1, l2]
</syntaxhighlight>
 
And add clauses for List and DottedList to the equal function:
<syntaxhighlight lang="haskell">
 
equal :: [LispVal] -> ThrowsError LispVal
equal [l1@(List arg1), l2@(List arg2)] = eqvList equal [l1, l2]
401 ⟶ 422行目:
return $ Bool $ (primitiveEquals || let (Bool x) = eqvEquals in x)
equal badArgList = throwError $ NumArgs 2 badArgList
</syntaxhighlight>
 
===Exercise 3===
 
407 ⟶ 428行目:
 
Room for improvement here!
<syntaxhighlight lang="haskell">
 
eval (List ((Atom "cond"):cs)) = do
b <- (liftM (take 1 . dropWhile f) $ mapM condClause cs) >>= cdr
419 ⟶ 440行目:
(Bool False) -> True
_ -> False
</syntaxhighlight>
 
Another approach:
<syntaxhighlight lang="haskell">
 
eval env (List (Atom "cond" : expr : rest)) = do
eval' expr rest
437 ⟶ 458行目:
Bool True -> eval env value
otherwise -> throwError $ TypeMismatch "boolean" cond
</syntaxhighlight>
 
===Exercise 4===