为什么构建列表推导元素会产生与约束中构造不同的结果?

时间:2018-01-10 04:49:38

标签: haskell list-comprehension

为什么这两种理解会产生不同的列表?

const bothrequests= Observable.combineLatest(
  this.http.get('https://testdb1.com/.json').map((res: Response) => res.json()),
  this.http.get('https://testdb2.com/.json').map((res: Response) => res.json())
)
bothrequests.subscribe(latestValues => {

});

这两个陈述不一样吗?结果如下所示:

l1 = [[x,-x] | x <- [1..]]

l2 = [pxnx | x <- [1..], pxnx <- [x,-x]]

4 个答案:

答案 0 :(得分:3)

l1 = [[x,-x] | x <- [1..]]

l2 = [pxnx | x <- [1..], pxnx <- [x,-x]]

这些列表推导等同于列表monad中的操作。在do表示法中,它们将是:

l1 = do
  x <- [1..]
  return [x,-x]

l2 = do
  x <- [1..]
  pxnx <- [x,-x]
  return pxnx

反过来,这些涉及绑定(>>=)和return的调用:

l1 = [1..] >>= \x -> return [x,-x]

l2 = [1..] >>= \x -> [x,-x] >>= \pxnx -> return pxnx

最后,我们可以为列表内联>>=return的定义:

l1 = concatMap (\x -> [[x,-x]]) [1..]

l2 = concatMap (\x -> concatMap (\pxnx -> [pxnx]) [x,-x]) [1..]

简化:

l1 = map (\x -> [x,-x]) [1..]

l2 = concatMap (\x -> [x,-x]) [1..]

因此l2concat l1完全相同。注意<-暗示“执行此操作”,对于列表monad意味着“迭代列表的每个元素”。如果您想要类似于l2但结果与l1相同的内容,则可以使用let代替<-

l2 = [pxnx | x <- [1..], let pxnx = [x,-x]]

这就像您在do表示法中写了以下内容:

l2 = do
  x <- [1..]
  let pxnx = [x,-x]
  return pxnx

答案 1 :(得分:3)

您的定义可以像其他人所指出的那样等效地编写,如

l1 = [[x,-x] | x <- [1..]]
   = [pxnx | x <- [1..], let pxnx = [x,-x]  ]
   = [pxnx | x <- [1..], pxnx <-   [[x,-x]] ]      -- NB

l2 = [pxnx | x <- [1..], pxnx <-    [x,-x]  ]

可以很容易看出,这些不是等效表达式。

问题归结为列表推导中<-的含义。

更一般地说,这可以用伪代码写成

l1 = for each x in [1..]:
        yield [x,-x]

   = for each x in [1..]:
        for each pxnx in [[x,-x]]:    -- NB: only _one_ element in this list
            yield pxnx

l2 = for each x in [1..]:
        for each pxnx in  [x,-x] :    -- NB: _two_ elements in this list
            yield pxnx

虽然两个for循环嵌套,但yield是&#34;相同&#34;:它指定正在构建的列表中的下一个元素。

所以你看,你做&#34;在约束&#34;中构建它。这是嵌套生成器

答案 2 :(得分:2)

让我们来看看这两种表达方式。

在第一个中我们可以看到,在列表理解的左侧,我们有一个包含两个元素x-x的列表,所以我们知道从列表理解中得到的列表某些类型[[a]]必须至少为深度2列表a(可能受某些数字类型限制)。

在列表理解的右侧,我们采用正整数1..并将它们逐个存储到x

因此,这个表达式应该给我们一个正面整数与他们的负面对应物配对的列表。

[[x,-x] | x <- [1..]]

在第二个中,我们可以看到左侧只包含一个变量,因此我们只能从中确定该列表必须至少为深度为1。

如果我们查看右侧,我们可以看到,就像在前面的表达式中一样,它将正整数存储到x中。但是,与前面的表达式不同,它将+ - 整数对逐个放入pxnx

这导致此表达式产生类型[a]的列表,而不是之前表达式的[[a]]类型。

此外,此列表包含所有正数及其负数,如前一个列表,但它将它们全部存储在同一个列表中,而不是将每个对存储在单独的列表中。

[pxnx | x <- [1..], pxnx <- [x,-x]]

这种差异是由于在pxnx <- [x,-x]中,值x-x被存储为逐个到pxnx中。而在第一个表达式中,它们被存储为组合列表。

答案 3 :(得分:1)

他们会产生不同的结果,因为<-不是=

这些是相同的:

l1 = [[x,-x] | x <- [1..]]

l2 = [pxnx | x <- [1..], let pxnx = [x,-x]]

pxnx <- [x, -x]松散地表示for pxnx in [x, -x],因此在您的情况下,我们会得到x-x中的每一个。