Haskell对Eq关系的列表理解

时间:2016-08-30 00:55:38

标签: haskell list-comprehension

我正在研究Haskell中的一个类,我们正在使用列表推导来实现等价关系,这有点令人困惑。例如,我无法理解在这种情况下究竟发生了什么。

exmp2 :: Reln -> [(Int, Int)]
exmp2 rs = [ (j, i) | (i,j) <- rs, (j,i) <- rs ]

VS

exmpl1 :: Reln -> [(Int, Int)]
exmpl1 rs = [ (j, i) | (i,j) <- rs ]

如果我使用上述......

exmpl1 [(1, 2), (2, 7), (1,9)]输出 [(2, 1), (7,2), (9,1)]

exmpl2 [(1, 2), (2, 7), (1,9)]输出  [(1,2), (2,7), (1,9), (1,2), (2,7), (1,9), (1,2), (2,7), (1,9)]

我已经阅读了列表推导教程等,但我不能为我的生活弄清楚为什么exmpl2确切地输出了什么。感谢

2 个答案:

答案 0 :(得分:3)

我觉得从一个更简单的例子中接近它可以更容易理解。

λ let chars = ['a', 'b', 'c']

λ let bools = [True, False]

λ [(c, b) | c <- chars, b <- bools]
[('a',True),('a',False),('b',True),('b',False),('c',True),('c',False)]

应该很清楚,我们已经制作了从c中获取一个chars和从b中获取一个bools的所有可能组合。

下一步是了解我可以使用(c, b)放置的任何表达式。该部分对结果列表中的项目的数量具有影响;无论我放在那里,我仍然会为来自cb的{​​{1}}和chars的每个组合提供一个项目。我甚至不需要实际使用boolsc(尽管显然你通常会使用实际代码)。

举个例子:

b

对于λ [ 7 | c <- chars, b <- bools] [7,7,7,7,7,7] 中的cchars中的b的每个组合,我都会生成bools。这是我列表中的六个7项目。

我也可以只引用7c中的一个,但它仍然不会改变我提出的项目数量:

b

我使用的唯一是来自λ [ b | c <- chars, b <- bools ] [True,False,True,False,True,False] 的{​​{1}}和True,但确定了列表的形状(有六个项目)通过“Falsebools”逻辑的每个组合。

现在我可以提取的一个稍微邪恶的技巧(在你的chars中使用)是因为我从不使用bools因为我用exmp2提取的变量名称无关紧要{{ 1}}。因为当我从c中提取时,我以后绑定chars,我可以在那里使用bbools 阴影中的b的第二个绑定第一个绑定;它实际上是一个完全独立的变量,但由于它们具有相同的名称,我现在只能引用它们中的一个。由于我只使用其中一个,这不会影响我正在尝试做的事情(但它确实使代码更难理解,因为你必须知道更多来确定哪个b绑定是影响我的列表中的值的那个:)

b <- bools

现在我们来看看你的例子:

b

这是来自λ [ b | b <- chars, b <- bools ] [True,False,True,False,True,False] 的{​​{1}}和exmp2 :: Reln -> [(Int, Int)] exmp2 rs = [ (j, i) | (i,j) <- rs, (j,i) <- rs ] 的{​​{1}}的每个组合。暂时忽略(i, j)rs;我们通过从同一个列表中选择两件事来获取我们得到的所有组合。因此,我们最终会得到输出列表中(j, i)中项目数的平方。

第一个rs案例绑定变量ij。但是第二种情况再次绑定了相同的变量,所以它们完全无关紧要。我们可以把它看成是:

rs

因此,对于(i, j) <- rs中我们不关心的一对的每种可能组合,以及来自i的另一对j,会生成exmp2 rs = [ (j, i) | _ <- rs, (j,i) <- rs ] 对。< / p>

这就是为什么在rs中你得到的输出包含3个序列(j, i)的副本;第一对rs一次,第二对(j, i)一次,第三对exmpl2 [(1, 2), (2, 7), (1,9)]一次。

答案 1 :(得分:2)

列表 - 理解语法也可以使用&#34; do&#34;来表达。符号,不那么神秘,也不易解释。

,例如,以下理解:

[ (j, i) | (i,j) <- rs, (j,i) <- rs ]

等同于以下表达式:

do
  (i, j) <- rs
  (j, i) <- rs
  return (j, i)

所以这里发生的是你首先从rs monad(在本例中为List)和desctructure中提取一个值,并将其分配给ij变量。然后再次提取值并以相反的顺序将其分配给相同的变量。因此,您将影响第一个作业。之后,您只需为输出monad(List)生成值。

由于第一个赋值被遮蔽,我们可以简单地绕过它,得到以下等价表达式:

do
  _ <- rs
  (j, i) <- rs
  return (j, i)

甚至:

do
  rs
  (j, i) <- rs
  return (j, i)

定义List monad,以便您浏览所有提取的所有可能组合。

在这种情况下,你会提取两次。结果,您将获得rs输入列表的双遍历。

用命令式语言看起来像这样:

function exmpl2(rs) {
  var results = [];
  for (a in rs) {
    for (b in rs) {
      results.push(b);
    }
  }
  return results;
}