【问题标题】:Im having an error with Haskell with cases that i cant find我在使用 Haskell 时遇到了我无法找到的错误
【发布时间】:2020-03-18 01:38:06
【问题描述】:

所以我试图创建这个函数 AgregarMon,它基本上将“Monomio”添加到“Polinomio” Monomio 最终会成为 Polinomio 中的一个元素,它是一个列表。一会儿你就会明白的了

type Monomio = (Int, Int)
type Polinomio = [Monomio]

agregarMon :: Monomio -> Polinomio -> Polinomio
agregarMon = \m p -> case m of{ (0,0) -> p;
                                 x -> case p of{[] -> [x];
                                                y:ys -> case (snd x == snd y) of { true -> case ((fst x + fst y)==0) of { true -> ys;
                                                                                                                          false -> (fst x + fst y , snd x):ys;}
                                                                                   false -> case snd x < snd y of{true -> y: agregarMon x ys;
                                                                                                                  false -> x:y:ys;}}}}

我一直在查看我的代码一个小时,但我找不到问题所在。错误说:

Polinomios.hs:45:140: error:
    Unexpected case expression in function application:
        case ((fst x + fst y) == 0) of
          true -> ys
          false -> (fst x + fst y, snd x) : ys
    You could write it with parentheses
    Or perhaps you meant to enable BlockArguments?
   |
45 |                                                                                                 y:ys -> case (snd x == snd y) of { true -> case ((fst x + fst y)==0) of { true -> ys;    |                                                                                                                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...

第 45 行是上面代码的第四行。对不起,如果我缺少信息,请让我现在。 以防万一,有人不知道,fst 和 snd 在 Prelude 上。 fst 从“Monomio”中获取第一个元素,然后获取第二个元素。 fst = (a,b)->a snd (a,b) -> b

【问题讨论】:

  • 老实说,基于布局的格式与手动花括号/分号的奇怪组合让我很难说出编译器认为你的代码意味着什么。如果您去掉所有{}; 字符并正常布局,会更容易理解。
  • 警告:true 不是True,它是一个和其他变量名称一样的变量名,它将匹配任何布尔值,包括False!启用警告以帮助 GHC 发现此类错误。
  • 每个人都在抱怨格式,但@chi 注意到了真正的问题

标签: function haskell recursion switch-statement haskell-prelude


【解决方案1】:
case (snd x == snd y) of { true ->

错了:true 这里只是一个变量名,与构造函数True 无关(大写的T!)。因此上面的 sn-p 等价于

case (snd x == snd y) of { x ->

因此,该模式匹配任何布尔值 TrueFalse。因此,永远不会考虑另一个分支 false -&gt; ...

我建议您打开警告,因为这样做会使 GHC 将第二个分支报告为冗余代码。编译器认为分支无用这一事实表明代码存在严重问题。

作为最后一点,在我的经验代码中

case something of
  True  -> a
  False -> b

不常见,因为我们可以写成

if something
then a
else b

在我看来,这更容易阅读。

【讨论】:

  • 非常感谢!我不敢相信我在这么简单的事情上浪费了这么多时间,我会尝试发出警告。我这样写是因为我就是这样被教导去做的,我在互联网上看到每个人都像你一样写它,这可能是有充分理由的,但我现在必须这样做。再次感谢!
  • @Jackal 即使if 更常见,使用case .. of True -&gt; ... ; False -&gt; ... 也是一个很好的学习练习。布尔值在 Haskell 中并不像在其他语言中那么重要,初学者倾向于过度使用 if 和偏函数,而 case 会更好。所以,学习case很重要。
【解决方案2】:

虽然您可以在紧要关头使用大括号和分号,但 Haskell 不是基于 C 的语言。 idiomatically uses indentation表示功能范围。

这样的东西会更惯用:

agregarMon :: Monomio -> Polinomio -> Polinomio
agregarMon = \m p -> case m of
                      (0,0) -> p
                      x -> case p of
                            [] -> [x]
                            y:ys -> case (snd x == snd y) of
                                      true -> case ((fst x + fst y)==0) of
                                                true -> ys
                                                false -> (fst x + fst y , snd x):ys
                                      false -> case snd x < snd y of
                                                true -> y: agregarMon x ys
                                                false -> x:y:ys

这会编译(尽管有警告,见下文),但我不知道它是否符合您的要求。 OP 代码无法编译,所以很明显 编译 的东西并不等价。

然而,我所做的只是删除所有大括号和分号,而是“修复”缩进。

在我看来,代码仍然缩进太多,但至少(除了一个例外)它保持在 80 个字符的左侧。宽代码可能会迫使人们水平滚动,这不会让你交到很多朋友。

以上代码在 GHCi 中加载,但带有警告:

[1 of 1] Compiling Q58986486        ( 58986486.hs, interpreted )

58986486.hs:14:49: warning: [-Woverlapping-patterns]
    Pattern match is redundant
    In a case alternative: false -> ...
   |
14 |                                                 false -> (fst x + fst y , snd x):ys
   |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

58986486.hs:15:39: warning: [-Woverlapping-patterns]
    Pattern match is redundant
    In a case alternative: false -> ...
   |
15 |                                       false -> case snd x < snd y of
   |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...

58986486.hs:17:49: warning: [-Woverlapping-patterns]
    Pattern match is redundant
    In a case alternative: false -> ...
   |
17 |                                                 false -> x:y:ys
   |                                                 ^^^^^^^^^^^^^^^
Ok, one module loaded.

因此,我不确定它是否按预期工作。

【讨论】:

    【解决方案3】:

    Mark Seemann 的版本还可以改进。

    首先,让mp 成为agregarMon 的实际参数:

    agregarMon :: Monomio -> Polinomio -> Polinomio
    agregarMon m p = case m of
                          (0,0) -> p
                          x -> case p of
                                [] -> [x]
                                y:ys -> case (snd x == snd y) of
                                          true -> case ((fst x + fst y)==0) of
                                                    true -> ys
                                                    false -> (fst x + fst y , snd x):ys
                                          false -> case snd x < snd y of
                                                    true -> y: agregarMon x ys
                                                    false -> x:y:ys
    

    现在我们可以在 agruments 上使用模式匹配来简化代码:

    agregarMon :: Monomio -> Polinomio -> Polinomio
    agregarMon (0, 0) p = p
    agregarMon x [] = [x]
    agregarMon (f, s) (y:ys) = case (snd x == snd y) of
                                true -> case ((fst x + fst y)==0) of
                                             true -> ys
                                             false -> (fst x + fst y , snd x):ys
                                false -> case snd x < snd y of
                                              true -> y: agregarMon x ys
                                              false -> x:y:ys
    

    条件检查的结果与case 进行模式匹配并不那么惯用,最好使用if 或守卫:

    agregarMon :: Monomio -> Polinomio -> Polinomio
    agregarMon (0, 0) p = p
    agregarMon x [] = [x]
    agregarMon x@(coefx, powx) (y@(coefy, powy):ys)
      | powx == powy = if coefx + coefy == 0
                       then ys
                       else (coefx + coefy, powx):ys
      | powx < powy = y:agregarMon x ys
      | otherwise = x:y:ys
    

    现在我们有一些简单的案例和一个复杂的案例,但即使是复杂的案例也不难阅读和理解(我什至假设您正在处理多项式、系数和幂)。

    你可以在那里运行它:https://repl.it/@Yuri12358/so-monomio-polinomio

    也许此链接将帮助您提高对在代码中进行分支的不同方法的理解:http://learnyouahaskell.com/syntax-in-functions#pattern-matching

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-05
      • 1970-01-01
      • 1970-01-01
      • 2011-07-06
      • 2020-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多