4

Haskellで書いているミニインタプリタの一部として、次のことを行う関数を書いています。の場合、( )eval (App e1 e2)を再帰的に評価し、結果を。に設定します。次に、Switch / Caseを使用して、のパターンを確認し、それがエラーでない場合は、再帰的に評価()し、その値をに設定します。これらの2つの値とを使用して、これらの値に別の関数()を適用します。e1eval e1v1v1e2eval e2v2v1v2appVals

eval :: Exp -> Error Val
eval (App e1 e2) = appVals v1 v2 where
    v1 = case (eval e1) of
        Error err -> Error "Not an application"
        /= Error err -> eval e1 = v1
    v2 = case (eval e2) of
        Error err -> Error "Not an application"
        /= Error err -> eval e2 = v2

私はそれを理解したかもしれないと思いますが、私はスイッチ/ケースの部分を正しく行ったかどうか完全にはわかりません。何かアイデア/提案はありますか?

4

4 に答える 4

12

Errorケースステートメントの2番目の部分は、最初の部分と一致するはずの-ではないことがすでにわかっているため、再テストを試みるべきではありません。(スキップし/= Error errます。)

eval e1 = v1最初に行った評価をやり直そうとします。あなたはそれをする必要はありません。

これがあなたが意図したことだと思います:

eval :: Exp -> Error Val
eval (App e1 e2) = case eval e1 of
    Error _ -> Error "Not an application"
    S v1    ->    case eval e2 of        -- nested case statement
     Error _ -> Error "Not an application"
     S v2    -> appVals v1 v2            -- match the S away

しかし、それはすべて少し醜いように思われるので、Gabriel Gonzalezから優れたアドバイスを受けて、からアプリケーションを作成しましょうError

instance Functor Error where
   fmap f (Error e) = Error e  -- pass through errors
   fmap f (S x)     = S (f x)  -- edit successes

たとえばfmap (+4) (Error "oops") = Error "oops"fmap (+4) (S 5) = S 9

これfmapがまったく新しい場合は、Functorsチュートリアルを読んでみませんか?

次に、Applicativeインスタンスを作成しましょう。Applicativeを使用すると、単純な関数のような複雑な関数を使用できます。import Control.Applicativeファイルを機能させるには、ファイルの先頭に配置する必要があります。

instance Applicative Error where
    pure x = S x   -- how to put ordinary data in
    S f     <*> S x     = S (f x)
    Error e <*> _       = Error e
    _       <*> Error e = Error e

ここで、エラーがなかった場合は、次のように定義します。

appVal' :: Val -> Val -> Val

eval' :: Exp -> Val
eval' (App e1 e2) = appVal' (eval' e1) (eval' e2)

アプリケーションを使用すると、で定義した配管を実行することを除いて<$>、少し同じように機能するものを使用できます。同様に、追加の配管を除いて、関数適用と少し似ています。$fmap<*>

eval :: Exp -> Error Val
eval (App e1 e2) = appVals <$> eval e1 <*> eval e2

これは、機能に焦点を合わせながら、舞台裏でエラーを処理するための優れたクリーンな方法です。

于 2012-11-18T21:40:30.333 に答える
4

昨日(モジュロ名前変更)であなたを残しました

eval (App e1 e2) = appVals <$> eval e1 <*> eval e2

これはほぼあなたが望むものです。違いは、2つの入力のいずれかがエラーである場合、それを特定のエラーメッセージに置き換えたいということです。(私が理解していないのはその理由です。)それでは、最後のビットだけを実行する関数を書いてみましょう!

butError :: String -> Error a -> Error a
butError message (Error _) = Error message
butError _       noError   = noError

evalこれで、句を次のように書き直すことができます。

eval (App e1 e2) = appVals <$> butError message (eval e1) <*> butError message (eval e2)
  where message = "Not an application"
于 2012-11-18T23:36:25.073 に答える
3

次のようなデータ型があると思います。

data Result a = Error String | Success a

問題の解決策は、次のいずれかです。

(a)ResultタイプをaにするMonad

(b)Either StringすでにMonadインスタンスがあるため、結果の代わりに使用します。

どちらを行う場合でも、次のように記述します。

eval :: SyntaxTree a -> Result a
eval (App ef ex) = do
    f <- eval ef
    x <- eval ex
    return (f x)

技術的には、Applicativeこの目的には十分であり、代わりに次のように書くことができます。

eval (App ef ex) = eval ef <*> eval ex
于 2012-11-18T22:03:17.650 に答える
2

で、あなたは選択肢のためのパターンcaseを持っている必要があります、あなたはそこを持つことはできません。/= Error err

また、式の=矢印の右側にを含めることはできないため、Haskellは無効です。->case-> eval e1 = v1

あなたの場合、それがではないことだけを気にしますError somethingが、最初のパターンが一致しなくなると、それは自動的に当てはまります。他に何が得られるかは気にしないので、可変パターンを使用できます。

eval (App e1 e2) = appVals v1 v2 where
    v1 = case (eval e1) of
        Error err -> Error "Not an application"
        other -> other
    v2 = case (eval e2) of
        Error err -> Error "Not an application"
        other -> other
于 2012-11-18T21:40:55.910 に答える