【问题标题】:what is wrong with if-then-else here?if-then-else 这里有什么问题?
【发布时间】:2012-02-13 22:53:05
【问题描述】:

我尝试过多种方式:if-then-else、守卫、case 语句,但我从未设法编译它。我认为从代码中很清楚我想要做什么。为什么这是不可能的,我必须做些什么才能弄清楚?

appendMsg :: String -> (String, Integer) -> Map.Map (String, Integer) [String] -> Map.Map (String, Integer) [String]
appendMsg  a (b,c) m = do
      let Just le1 = length . concat <$> Map.lookup (b,c) m
          le2 = le1 + length a
      if le2 < 1400 then (let m2 = Map.adjust (x ++ [a]) (b, c) m) else (print (le1, le2))
      return (m2)

我在这里收到的错误消息是parse error on input `)'。如果我更改括号,我会得到parse error on input `else'。如果我在 else 路径中说 (let m2 = m),那么我会再次遇到括号错误。

我试图实现的是,如果 le2 创建

f x = x ++ [a]
m2 = Map.adjust f (b, c) m

如果 le2 > 1400 则应调用另一个函数,该函数采用与 appendMsg 相同的参数,并且 appendMsg 应该不返回任何内容。

另一个问题是,一旦根据以下所有建议正确设置:

if le2 &lt; 1400 then let m2 = Map.adjust (++ [a]) (b, c) m in return (m2) else return ()

我收到一个错误提示

 No instance for (Monad (Map.Map (String, Integer)))
      arising from a use of `return'

顺便说一下,当我使用警卫时,我最终遇到了同样的错误。问题是我在剩下的代码中对这种奇怪的类型几乎无能为力。我可以以任何方式将此类型转换回 (Map.Map (String, Integer) [String]) 或完全避免它吗?

【问题讨论】:

  • 一般来说,包含您收到的错误消息是个好主意。
  • 如果le2 &gt;= 1400等于m2
  • 如果遵循else路径会返回什么?
  • if-then-else 语句的两个分支必须具有相同的类型。您的语句的 then 分支实际上根本没有类型,因为您有一个没有任何后续语句的 let-binding,而 else 分支实际上是 IO () 类型,它在你的功能。
  • 如果 Haskell 报告“严重错误”,我会说这是厚脸皮(抱歉,英国笑话)。

标签: haskell


【解决方案1】:

let 必须跟在 in 之后,或者直接在 do 块内。 let 不能是 do 块中的最后一条语句。这些规则是因为创建一个立即超出范围而没有任何使用机会的变量是没有意义的。

在您的代码中,您在ifthen 分支内创建了一个名为m2 的变量,但您从不使用它。您确实(尝试)在if 之外使用它,但在if 之外m2 不存在。您不能使用在 if 之外的 if 中创建的变量。当if 条件为假时,m2 的值应该是多少?

【讨论】:

  • 如果 le2
  • @JFritsch:你的代码总是返回m2。如果le2 &gt; 1400,您期望会发生什么?
  • @KevinBallard 在这种情况下,我想调用另一个函数来刷新我的队列。打印的只是虚拟代码。
  • @JFritsch 关键是变量只存在于then-branch 中。如果将return 放在then-branch 中(并在else-branch 中输入正确的内容),它会起作用。
  • @sepp2k 现在明白了。但是我怎样才能实现我需要做的事情:如果 le2
【解决方案2】:

您正在使用do,其结果必须是Monad 实例,但您的函数类型表明它返回的Map 不是Monad 的实例。如果你真的想在le2 &gt;= 1400 时返回Nothing,那么你必须在le2 &lt; 1400 时返回Just ...,你的函数可能如下所示:

import qualified Data.Map as Map
import Control.Monad

appendMsg :: String -> (String, Integer) ->
             Map.Map (String, Integer) [String] ->
             Maybe (Map.Map (String, Integer) [String])
appendMsg  a k m = do
      v <- Map.lookup k m
      let l = length . concat $ v 
          l' = l + length a
      guard $ l' < 1400
      return $ Map.insert k (v ++ a) m

请注意,如果在Map 中找不到开头的键,此函数将返回Nothing...

【讨论】:

  • 这里的 $ 会缩写为 () ?
  • $ 是函数应用的低优先级形式。 f a $ g b 等价于 f a (g b)。它主要用于消除括号,但当您想将函数应用程序视为一个函数时也很有用(例如map ($ a) fs,它将fs 中的每个函数应用于参数a,并将结果作为列表返回)。
  • Monad 实例的问题真的可以解决吗?这难道与使用 'guard' 和 MonadPlus 无关吗?
  • 有点。你可以做x = do 3,它可以工作,即使3 不是Monad 的一个实例。但是,如果do 中有两个“语句”,它们将通过&gt;&gt;=(或&gt;&gt;)连接,这将要求它们都是相同的Monad 实例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多