48時間でSchemeを書こう/評価: 第二部２

# 参考URL
[48時間でSchemeを書こう/評価: 第二部](https://ja.wikibooks.org/wiki/48時間でSchemeを書こう/評価:_第二部)

#ソース
https://github.com/0zawa/scheme_in_48_hours/blob/master/evaluation_2.hs


# 条件分岐
if文を評価できるようにします。

```hs
eval (List [Atom "if", pred, conseq, alt]) = 
    do result <- eval pred
       case result of
         Bool False -> eval alt
         otherwise -> eval conseq
```

やっていることは単純で、Listの先頭がAtomの"If"なら
リストの２つめのpredを評価して、
その結果がBool Falseならリストの４番めを評価、
それ以外ならリストの３番めを評価、
としているだけです。
例として、

```hs
(if (> 2 3) "no" "yes")
```

上記のようなリストの場合、predが(>2 3)
predがBool Falseのとき評価されるのが"yes"
predがそれ以外のとき評価されるのが"no"
になります。

# car
carはSchemeでリストの先頭を返す関数です。
Haskellではheadに相当します。

```hs
car :: [LispVal] -> 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
```

List,DottedListなら先頭の要素を返し、
それ以外ならエラーにしています。

# cdr
cdrは「クダー」と読むようです。
Schemeでリストの先頭以外の要素を返す関数です。
Haskellではtailになります。

```hs
cdr :: [LispVal] -> 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
```

Listの場合は単純に先頭以外の[LispVal]を
ListコンストラクタでLispValにしています。
DottedListは

```hs
DottedList [LispVal] LispVal
```
の[LispVal]が要素１つのリストならLispValを返し、
[LispVal]が要素２つ以上のリストならそのリストの
先頭以外の部分とLispValをDottedListコンストラクタで再構築して
LispValにします。
それ以外のケースは全てエラーにします。


# cons
consは「コンス」と読みます。
Haskellでは「:」にあたります。

この実装は少しずつ、順を追ってみていきます。

まず、任意のLispValと、空リストを持つListをconsすると、
List [x1]になります。

```hs
cons :: [LispVal] -> ThrowsError LispVal
cons [x1, List []] = return $ List [x1]
```
次に、任意のLispValと中身のあるListをconsすると
LispValとListの中身を連結して、ListコンストラクタでLispValにします。

```hs
cons [x, List xs] = return $ List $ x : xs
```

任意のLispValとDottedListにconsを適用した場合は
LispValとDottedListの先頭を連結して、
DottedListコンストラクタでLispValにします。

```hs
cons [x, DottedList xs xlast] = return $ DottedList (x : xs) xlast
```
上記以外のパターンで、LispVal２つのリストが来た場合は
DottedListコンストラクタでLispValにします。

```hs
cons [x1, x2] = return $ DottedList [x1] x2
```

ここに来るのはx2がListでもDottedListでも無い場合全てなので、
x1がListやDottedListの場合も含まれます。

それ以外のケースはエラーにします。

```hs
cons badArgList = throwError $ NumArgs 2 badArgList
```

#eqv
Listの２つの要素が等しいかを判定するeqv関数を定義します。

```hs
eqv :: [LispVal] -> 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) && 
                                                    (all eqvPair $ zip arg1 arg2)
    where eqvPair (x1, x2) = case eqv [x1, x2] of
                               Left err -> False
                               Right (Bool val) -> val
eqv [_, _] = return $ Bool False
eqv badArgList = throwError $ NumArgs 2 badArgList
```

かなり長いですが、Bool,Number,String,Atomの比較は単純です。
それぞれの中身を==で比較しているだけです。

DottedListの場合はListに変換してからeqvを再帰させて
判定します。

Listの場合は複雑なので、少しずつみていきます。
まずlengthでリストの要素数を比較します。
リストの要素数が等しければ、

```hs
all eqvPair $ zip arg1 arg2
```

を実行します。
all関数は、リストを取る関数とリストが与えられて、
全てのリストの要素が、関数でTrueを返すかどうかを判定します。

言葉にすると少しわかりにくいので例を示します。

```hs
> all even [2,4,6,8]
True
> all even [2,4,6,7]
False
> all even [1,4,6,8]
False
```

evenは偶数かどうかを判定する関数です。

eqv関数内でallに与えられるリストは

```hs
zip arg1 arg2
```

です。
これはarg1とarg2のリストの各要素をタプルにしたリストを返します。

```hs
> zip [1,2,3] [4,5,6]
[(1,4),(2,5),(3,6)]
```

eqvPair関数は

```hs
where eqvPair (x1, x2) = case eqv [x1, x2] of
                               Left err -> False
                               Right (Bool val) -> val
```
と定義されている通り、各タプルの要素をeqvで評価し、
その結果を返します。

eqv関数はそれ以外の場合はエラーになります。