【问题标题】:Haskell Switch/Case UseHaskell 开关/机箱使用
【发布时间】:2012-11-18 21:36:11
【问题描述】:

作为我在 Haskell 中编写的迷你解释器的一部分,我正在编写一个执行以下操作的函数:如果是 eval (App e1 e2),我想递归评估 e1 (eval e1),设置结果到v1。然后使用 Switch/Case,我想检查 v1 的模式,如果不是错误,则递归评估 e2 (eval e2) 并将该值设置为 v2。使用这两个值 v1v2,然后我对这些值应用另一个函数 (appVals)。

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

我想我可能已经弄清楚了,但我不完全确定我是否正确地完成了开关/外壳部分。有什么想法/建议吗?

【问题讨论】:

标签: haskell recursion switch-statement


【解决方案1】:

您的 case 语句的第二部分不应尝试重新测试,因为您已经知道它不是 Error - 与第一部分匹配。 (跳过/= 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 tutorial

接下来让我们创建一个 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)

通过应用程序,我们可以使用&lt;$&gt;,它的工作方式有点像$,除了它执行您在fmap 中定义的任何管道。 同样,&lt;*&gt; 工作起来有点像 函数应用程序,除了额外的管道,所以我们可以定义

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

这是处理幕后错误同时专注于功能的一种很好的干净方式。

【讨论】:

  • 我在上面添加了一些其他信息供您查看。这有帮助吗?
  • @NuNu 是的。谢谢。我已经回复了。
【解决方案2】:

I left you yesterday(模重命名)与

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

这几乎就是您想要的,不同之处在于如果两个输入中的任何一个出现错误,您希望用特定的错误消息替换它。 (我不明白为什么。)所以让我们编写一个函数来完成最后一点!

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"

【讨论】:

    【解决方案3】:

    我假设你有一些看起来像这样的数据类型:

    data Result a = Error String | Success a
    

    您的问题的解决方案是:

    (a) 让您的Result 输入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
    

    【讨论】:

      【解决方案4】:

      case 中,您需要有模式作为替代品,您不能在其中有/= Error err

      此外,case 表达式中的箭头 -&gt; 右侧不能有 =,因此 -&gt; eval e1 = v1 不是有效的 Haskell。

      在你的情况下,你只关心它不是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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-11-03
        • 1970-01-01
        • 2020-04-17
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多