2

式ソルバーを実装していますが、パターン マッチングに問題があります。次のコードがあります

data Expression a where
                Const   ∷  Int → Expression Int
                Add ∷  Expression Int → Expression Int → Expression Int
                Sub ∷  Expression Int → Expression Int → Expression Int


eval ∷  Expression a → a
eval (Const a) = a

eval (Add exp1 exp2) = (val1 + val2)
  where
    val1 = eval exp1
    val2 = eval exp2


eval (Sub exp1 exp2) = (val1 - val2)
  where
    val1 = eval exp1
    val2 = eval exp2

しかし、 eval Add と eval Sub は非常に似ているため、別の操作が必要になる可能性があるため、より一般的な実装を行うことを考えていますが、いくつかの問題があります。私は好きなことをしているのに

data Op = Add | Sub

data Expression a where
                Const   ∷  Int → Expression Int
                Op ∷  Expression Int → Expression Int → Expression Int

eval (Op exp1 exp2) = case Op of
                           Add → (val1 + val2)
                           Sub → (val1 - val2)
                      where
                        val1 = eval exp1
                        val2 = eval exp2 

しかし、うまくいきません。このようなことは可能ですか?前もって感謝します

4

3 に答える 3

7

Opデータコンストラクターと型の両方として定義しているため、これは機能しません。型Opには2つのコンストラクターAddとがありますSubが、 Expression型にはOpコンストラクターがあります。このコードは2つを混同しています。

関数のcaseステートメントはeval値の一致を試み ますOpOp、このコンテキストでは2つの引数を取るコンストラクターであるため、パターン一致を行うことはできません。私はあなたがこのような何かをしようとしているのではないかと思います

data Op = Add | Sub

data Expression a where
                Const ::  Int -> Expression Int
                Op ::  Op -> Expression Int -> Expression Int -> Expression Int

eval (Const c)         = c
eval (Op op exp1 exp2) = case op of
                           Add -> (val1 + val2)
                           Sub -> (val1 - val2)
                      where
                        val1 = eval exp1
                        val2 = eval exp2

実行する操作を示すフィールドをOpコンストラクターに含める必要があります。とにかくその操作に一致する必要があるので、元の定義に固執する方がおそらく良いでしょう Expression

より単純で拡張が容易な別の可能性は、次のようなものです。

data Expression a where
    Const ::  Int -> Expression Int
    Op    ::  (a -> b -> c) -> Expression a -> Expression b -> Expression c

eval :: Expression a -> a
eval (Const c)        = c
eval (Op f exp1 exp2) = f (eval exp1) (eval exp2)

ここで、Opは実際の関数をそれでラップします。ただし、式を印刷して、それがどの関数に対応するかを知るなど、すばらしいことはできません。

于 2013-02-08T18:50:19.890 に答える
4

コメントのリフ:

data Op a b c where
    Add :: Op Int Int Int
    Sub :: Op Int Int Int
    Less :: Op Int Int Bool

interpretOp :: Op a b c -> a -> b -> c
interpretOp Add = (+)
interpretOp Sub = (-)
interpretOp Less = (<)

data Expression a where
    Const :: Int -> Expression Int
    Op :: Op a b c -> Expression a -> Expression b -> Expression c

eval :: Expression a -> a
eval (Const x) = x
eval (Op op a b) = interpretOp op (eval a) (eval b)
于 2013-02-08T20:51:15.067 に答える
0

ルキの答えをリフする:

{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, GADTs #-}
class OpLike op a b where
    type Result op a b
    interpret :: op -> a -> b -> Result op a b

data Expression a where
    Const :: a -> Expression a
    Op :: OpLike op a b => op -> Expression a -> Expression b -> Expression (Result op a b)

eval :: Expression a -> a
eval (Const x) = x
eval (Op op a b) = interpret op (eval a) (eval b)

これで、luquiのOpデータ型などを変更することなく、プログラムの任意の時点で演算子を追加できます。これは非常に工夫された例です:

data Add = Add
add x y = Op Add x y

instance OpLike Add Int Int where
    type Result Add Int Int = Int
    interpret Add x y = x + y

instance OpLike Add Int Bool where
    type Result Add Int Bool = String
    interpret Add x y = if y then reverse (show x) else show x    

example = (Const (3::Int) `add` Const (10::Int)) `add` (Const True)

exampleタイプがExpression Stringあり、eval使用します"31":-)

于 2013-02-09T16:52:40.047 に答える