Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 列表理解中的where子句_Haskell_List Comprehension - Fatal编程技术网

Haskell 列表理解中的where子句

Haskell 列表理解中的where子句,haskell,list-comprehension,Haskell,List Comprehension,以下两个公式的区别是什么 cp [] = [[]] cp (xs:xss) = [x:ys | x <- xs, ys <- cp xss] ---------------------------------------------- cp [] = [[]] cp (xs:xss) = [x:ys | x <- xs, ys <- yss] where yss = cp xss cp[]=[]] cp(xs:xss)=[x:ys | x好吧

以下两个公式的区别是什么

cp [] = [[]]
cp (xs:xss) = [x:ys | x <- xs, ys <- cp xss]
----------------------------------------------
cp [] = [[]]
cp (xs:xss) = [x:ys | x <- xs, ys <- yss]
              where yss = cp xss
cp[]=[]]

cp(xs:xss)=[x:ys | x好吧,一个简单的去糖过程应该是:

cp [] = [[]]
cp (xs:xss) = concatMap (\x -> concatMap (\ ys -> [ x:ys ]) (cp xss)) xs
----------------------------------------------
cp [] = [[]]
cp (xs:xss) = let yss = cp xss in concatMap (\x -> concatMap (\ ys -> [ x:ys ]) yss) xs
如您所见,在第一个版本中,调用
cp xss
在lambda中。除非优化器移动它,否则这意味着每次调用函数
\x->concatMap(\ys->[x:ys])(cp xss)
时,它都会被重新计算。通过将其浮出,我们可以避免重新计算


同时,GHC确实有一个优化过程,可以将昂贵的计算从这样的循环中浮出来,因此它可以自动将第一个版本转换为第二个版本。您的书中说,第二个版本“保证”只计算一次
cp xss
的值,因为如果表达式计算昂贵,编译器将通常情况下,在内联它时会非常犹豫(将第二个版本转换回第一个版本)。

当然,这两个定义在表示相同值的意义上是等价的

在操作上,它们在按需调用评估下的共享行为上有所不同。jcast已经解释了原因,但我想添加一个不需要显式取消列表理解的快捷方式。规则是:在语法上可能依赖于变量
x
的任何表达式都将在每次调用时重新计算变量
x
绑定到某个值的时间,即使表达式实际上不依赖于
x

在您的情况下,在第一个定义中,
x
cp-xss
出现的位置处于作用域内,因此
cp-xss
将针对
xs
的每个元素
x
重新计算一次。在第二个定义中,
cp-xss
出现在
x
的作用域之外,因此只计算一次

然后适用通常的免责声明,即:

  • 编译器不需要遵循按需调用求值的操作语义,只需要遵循指称语义。因此,它可能会比基于上述规则的预期计算次数更少(向外浮动)或更多(向内浮动)

  • 一般来说,共享越多越好并不正确。例如,在这种情况下,这可能并不更好,因为
    cp xss
    的大小增长速度与最初计算它所需的工作量一样快。在这种情况下,从内存读回值的成本可能超过重新计算值的成本(由于缓存层次结构和GC)

相关: