【问题标题】:Set builder notation error for my own zip implementation为我自己的 zip 实现设置构建器符号错误
【发布时间】:2015-04-13 20:59:04
【问题描述】:

所以我现在正在学习 haskell,但我无法理解我在以下模拟 zip 的函数中做错了什么

1.ziplike xs ys = [(x,y)|c<-[0..(min (length xs) (length ys))-1],x<-xs!!c,y<-ys!!c]

2.ziplike xs ys = [(xs!!c,ys!!c)|c<-[0..(min (length xs) (length ys))-1]]

现在,我知道正确答案是数字 2,但我不明白为什么呼叫 ziplike [1,2,3] ['a', 'b', 'c', 'd'] 时数字 1 是错误的。我认为这是因为它试图为单个字符选择索引,但我不知道为什么。

错误是“无法将预期类型‘[t1]’与实际类型‘Char’匹配”

【问题讨论】:

  • x &lt;- xs !! c 仅在 xs !! c 是一个列表时有效。

标签: haskell


【解决方案1】:

第一个近似值:

如果e::[a],
并且x &lt;- e 出现在列表理解中| 的右侧,
然后x :: a 绑定到任何地方。

这会导致您的情况出现一些问题。你有ys :: [Char]c :: Int,所以ys!!c :: Char。因此:

我们有ys!!c :: Char
并且y &lt;- ys!!c 出现在列表理解中| 的右侧,
所以y :: ???无论它被绑定到哪里。

但是现在我们在试图写出关于y 应该有什么类型的结论时遇到了困难:Char 不是as 的列表,无论我们选择什么a

有几种可能的修复方法;一种是使用let 而不是&lt;-,如

ziplike xs ys = [(x,y)|c<-[0..min (length xs) (length ys)-1],let x=xs!!c; y=ys!!c]

【讨论】:

  • 所以,我还在学习 Haskell,我还不完全熟悉行话(即::)。根据我的理解,您是说 e 需要是任意元素的列表,但由于 ys!!c 是 char 类型,而不是列表,它本质上是行不通的?因此,通过使用let,我们可以强制 x 和 y 为变量。我的理解正确吗?
  • @AnubhawArya 您可以将:: 发音为“有类型”。你的第二句话是正确的。 xy 是变量,无论您使用 let 还是 &lt;- 绑定它们;不同之处在于let 只是为表达式提供了另一个名称,而&lt;- 也有一些不那么琐碎的含义:x &lt;- e 允许x 依次采用列表e 中的每个元素的值.
【解决方案2】:

这是一个类型错误。

当你写“x from xs get-index c”(即x &lt;- xs !! c)时,你“来自”xs !! c 的东西不一定是一个列表。这是一个技术细节,但它很重要。 “从”箭头来自 monad syntax/do-notation。列表推导式实际上只是一个专门用于 List monad 的 do 表达式;所以箭头 &lt;- 的右侧需要是 List monad 中的一个列表。

首先,您可以通过使用单例列表“作弊”来解决此问题,例如:

ziplike xs ys = [ (x,y) | c <- [0 .. min (length xs) (length ys) - 1], 
                          x <- [xs !! c], 
                          y <- [ys !! c]]

所以这些左箭头不是“让”变量绑定,但它们产生笛卡尔积:但 n 事物与 1 事物与 1 事物的笛卡尔积只是 n * 1 * 1 == n 事物。所以这很棒,虽然有点奇怪并且可能效率低下。

要做你想做的事情(在列表理解中绑定 x 和 y),你还可以编写如下内容:

ziplike xs ys = [let x = xs !! c; y = ys !! c in (x, y) 
                | c <- [0 .. min (length xs) (length ys) - 1]]
-- or --
ziplike xs ys = [(x, y) 
                | c <- [0 .. min (length xs) (length ys) - 1],
                  let x = xs !! c, let y = ys !! c]
-- or --
ziplike xs ys = [(x, y) 
                | c <- [0 .. min (length xs) (length ys) - 1],
                  let x = xs !! c; y = ys !! c]

请注意,这些都是用逗号附加的 do-notation 想法。当然,所有这些看起来都比

ziplike xs ys = [(xs !! c, ys !! c) | c <- [0..min (length xs) (length ys) - 1]

做同样的事情。

话虽如此,所有这些东西zip 函数的递归字符效率低得多:如果我将列表大小加倍,您的实现需要 4 倍的时间来处理整个清单; zip 只需要两倍的时间。所以在你的编程中要注意这个“隐藏的 O(n2) 因素”。

【讨论】:

  • 您介意解释一下关于笛卡尔积的一段吗?我真的不明白:(
  • [1,2,3] 和 [4,5,6] 的Cartesian product 是对 [(1,4), (1,5), (1,6 ), (2,4), (2,5), (2,6), (3,4), (3,5), (3,6)]。您可以通过写[(x, y) | x &lt;- [1,2,3], y &lt;- [4, 5, 6]] 在 Haskell 中获得这个笛卡尔积。两个集合 A 和 B 的笛卡尔积具有基数 |A × B| = |A| |B|,即 A 和 B 的基数的乘积。如果其中一个集合仅包含一个元素,例如 {1, 2, 3} × {4},那么将有 3 个元素结果列表,您可以将相邻的 4 视为“局部变量”。
猜你喜欢
  • 2017-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多