【问题标题】:Haskell List Comprehension Non-exhaustive pattern when calling more than one parameterHaskell List Comprehension 调用多个参数时的非详尽模式
【发布时间】:2016-01-22 18:05:01
【问题描述】:

首先,我创建了一个类型 StudentMark,它是一个元组,首先是一个字符串,其次是一个 Int。

type StudentMark = (String, Int)

这是我的 capMarks 函数:

capMarks :: [StudentMark] -> [StudentMark]
capMarks [cMarks] = [(st, mk) | (st, mk) <- [capMark cMarks]]

这是我的 capMark 函数:

capMark :: StudentMark -> StudentMark
capMark (st, mk)
    |   mk > 39   =   (st, 40)
    |   mk < 40   =   (st, mk)

它应该返回:

[("Jo", 37), ("Sam", 40)]

来自:

capMarks [("Jo", 37), ("Sam", 76)]

但只有当我在函数中输入 1 个参数时才会返回正确和预期的响应,例如:

capMarks [("Jake", 50)]

或者

capMarks [("Jake"), 30]

但是使用两个(或更多)应该只是告诉我 capMarks 函数中有一个非穷举模式。

【问题讨论】:

  • 如果我的任何术语有问题,请多多包涵,因为我是 Haskell 和 stackoverflow 的新手
  • 让我们后退一步。你想用这些功能完成什么?这段代码的目的是什么?您实际上想用这段代码解决什么问题?
  • 它应该返回元组,其中超过 40 的整数将根据 capMark 函数返回为 40。即 capMark ("Steve", 100) 将返回 ("Steve", 40) 所以 capMarks 函数应该做的是使用 capMark 函数使用列表理解对元组列表执行此操作。

标签: haskell list-comprehension non-exhaustive-patterns


【解决方案1】:

让我们分析一下你的capMarks函数:

capMarks :: [StudentMark] -> [StudentMark]
capMarks [cMarks] = [(st, mk) | (st, mk) <- [capMark cMarks]]

首先capMarks [cMarks] = ...是一个模式匹配。这匹配包含单个元素的列表。我假设你想对整个列表做一些事情,所以把它改成capMarks cMarks = ...

接下来... [(st, mk) | (st, mk) &lt;- [capMark cMarks]] 会将capMark 函数应用于原始模式匹配方案中的唯一元素,然后将结果作为列表的唯一元素。您似乎想将capMark 应用于列表的每个元素。所以如果我们按照前面的建议,你需要做类似... [capMark mark | mark &lt;- cMarks]的事情。这与前面所说的完全一样:将capMark 应用于cMarks 列表的每个元素。

最终版本:

capMarks :: [StudentMark] -> [StudentMark]
capMarks cMarks = [capMark mark | mark <- cMarks]

或者,您还可以使用模式匹配和显式递归:

capMarks [] = []  
capMarks (x:xs) = capMark x : capMarks xs

第一行说capMarks 应用于一个空列表是一个空列表。第二行表示capMarks 应用于至少有一个元素的列表将capMark 应用于第一个元素,然后递归地将capMarks 应用于列表的其余部分。

这是 Haskell 中的一种常见模式,以至于有一个名为 map 的函数可以概括它。使用map 非常简单:

capMarks cMarks = map capMark cMarks

map 具有(a -&gt; b) -&gt; [a] -&gt; [b] 类型,这意味着它接受一个函数和一个列表并返回一个列表。 (ab 只是告诉编译器哪些类型必须相同。)map 然后将该函数应用于输入列表中的每个元素。

最终您将了解偏函数应用和无点风格。有了这两个概念,使用map的版本可以稍微简化一下:

capMarks = map capMark

暂时不要太担心这个。为了完整起见,我只是在此处添加它。

【讨论】:

  • 代码学徒,我看到了我现在遇到的问题!我认为我的 capMark 函数必须在理解的模式匹配方面。这现在更有意义了,谢谢。
  • @GarethAllen-Stringer 我用更多解释编辑了我的问题,包括一些替代实现。我希望这有帮助。祝你学习 Haskell 好运。
  • 啊,我从您的示例中看到了 map 的工作原理。我们还没有被教导过,但我会记住它的未来,因为它看起来会得到很多使用。非常感谢您的完整性。我见过(x:xs),但不明白它的含义或工作原理。
  • @GarethAllen-Stringer 我相信你最终会在你的课堂上学到这个。 (x:xs) 作为函数参数是一种模式,它匹配具有至少一个元素的列表。 x 是列表的第一个元素,xs 是列表的其余部分,甚至可能是一个空列表。
【解决方案2】:

您应该检查模式匹配在 Haskell 中是如何工作的。

capMarks [x] 只会匹配一个包含一个元素的列表。您可能想要的是 capMarks myList = [ ... | ... &lt;- f myList] 之类的东西,或者以递归方式定义其余的情况。

例如

capMarks [] = []
capMarks x:xs = capMark x : capMarks xs

这个简化的“版本”适用于拥抱

capMarks :: [Integer] -> [Integer]
capMarks xs = [(*) 2 x | x <- xs]

【讨论】:

  • 我的 Haskell 生锈了 :-/
  • 我对它的大部分内容都有所了解,但现在它调用了以前的函数,存在很多关于不匹配类型的问题。不过,我遵循您的简化版本。
  • 在我看来,问题在于变量 cMarks 是一个元组列表 (StudentMark),但函数 capMark 只会采用一个 StudentMark,而不是它们的列表。
  • 在 haskell 中,您可以考虑各种类型。 capMark 只能应用于学生,所以如果你要使用理解来提取列表中的元素,你应该将capMark 应用于列表中的元素。想想我刚才给你看的简化版本,如果你认为(*) 2capMark...你会怎么写 capMarks。
  • Ale,感谢您对列表理解的解释。非常感谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多