seq在Haskell中实际做什么?
从我读到的 它的操作如下:当计算seq在Haskell中实际做什么?,haskell,functional-programming,lazy-evaluation,order-of-execution,weak-head-normal-form,Haskell,Functional Programming,Lazy Evaluation,Order Of Execution,Weak Head Normal Form,从我读到的 它的操作如下:当计算seq表达式时,它强制计算其第一个参数,然后返回其第二个参数。它实际上对第一个参数没有任何作用:seq仅作为强制计算该值的一种方式存在 我之所以强调这一点,是因为对我来说,这意味着这两件事发生的顺序 从我读到的 如果a位于底部,则序列a b的值位于底部,否则等于b。换句话说,它将第一个参数a计算为弱头范式(WHNF)。通常引入seq是为了通过避免不必要的惰性来提高性能 关于求值顺序的说明:表达式seq A b不保证将在b之前求值A。seq提供的唯一保证是在seq返
seq
表达式时,它强制计算其第一个参数,然后返回其第二个参数。它实际上对第一个参数没有任何作用:seq
仅作为强制计算该值的一种方式存在
我之所以强调这一点,是因为对我来说,这意味着这两件事发生的顺序
从我读到的
如果a
位于底部,则序列a b
的值位于底部,否则等于b
。换句话说,它将第一个参数a
计算为弱头范式(WHNF)。通常引入seq是为了通过避免不必要的惰性来提高性能
关于求值顺序的说明:表达式seq A b
不保证将在b
之前求值A
。seq
提供的唯一保证是在seq
返回值之前,将对a
和b
进行评估。特别是,这意味着可以在a
之前对b
进行评估。[……]
此外,如果我从那里点击#Source
链接,页面不存在,因此我看不到seq
的代码
这似乎与下面的评论一致:
无法在普通Haskell中定义[…]seq
另一方面(或实际上,在同一方面),另一条评论是:
“real”在GHC.Prim中定义为seq::a->b->b;seq=设x=x在x中
这只是一个虚拟定义。基本上,seq
是由编译器专门处理的语法
有人能解释一下这个话题吗?特别是在以下方面:
- 什么来源是正确的
的实现真的不能在Haskell中写入吗?seq
- 如果是的话,这意味着什么?它是原始的吗?这告诉了我
实际做了什么seq
- 如果是的话,这意味着什么?它是原始的吗?这告诉了我
- 在
保证在seq a中,至少在
使用b
的情况下,ba
之前进行评估,例如b
seq a(a+x)
- 现实世界中的哈斯克尔错了,你引用的所有其他东西都是正确的。如果您非常关心评估顺序,请改用
pseq
。seq
在两个thunk之间引入了人工数据依赖关系。通常,thunk只有在模式匹配需要时才被迫求值。如果thunka
包含{…}的表达式案例b,则强制a
也强制b
。因此,这两者之间存在依赖关系:为了确定a
的值,我们必须评估b
│
┌─▼───────┐
│ seq a b │
└─┬─────┬─┘
│ │
┌─▽─┐ ┌─▼─┐
│ a │ │ b │
└───┘ └───┘
seq
指定任意两个thunk之间的这种关系。当强制执行seq c d
时,除了强制执行d
之外,还强制执行c
。请注意,我之前没有说过:根据标准,实现可以在d
之前强制c
,或者在c
之前强制d
,甚至可以强制执行它们的混合。只要求如果c
没有停止,那么seq cd
也不会停止。如果要保证评估顺序,可以使用pseq
下图说明了不同之处。黑箭头(▼) 表示实际的数据依赖关系,可以使用大小写表示这种依赖关系;白色箭头(▽) 指示人为依赖项
- 强制执行
seq a b
必须同时强制执行a
和b
│
┌─▼───────┐
│ seq a b │
└─┬─────┬─┘
│ │
┌─▽─┐ ┌─▼─┐
│ a │ │ b │
└───┘ └───┘
- 强制
pseq a b
必须强制b
,后者必须首先强制a
│
┌─▼────────┐
│ pseq a b │
└─┬────────┘
│
┌─▼─┐
│ b │
└─┬─┘
│
┌─▽─┐
│ a │
└───┘
目前,它必须作为一个内在的实现,因为它的类型,for all a b.a->b->b
,声称它适用于任何类型a
和b
,没有任何约束。它过去属于一个类型类,但由于类型类版本被认为具有较差的ergo,它被删除并成为一个原语nomics:addingseq
试图修复一个深度嵌套的函数调用链中的性能问题,需要在链中的每个函数上添加一个样板seq
约束(我更喜欢明确性,但现在很难更改)
因此,seq
,以及它的语法糖,比如data
类型中的严格字段或模式中的BangPatterns
,就是通过将某个内容附加到将要评估的其他内容的评估中来确保对其进行评估。经典示例是foldl'
。这里,seq
确保递归调用是强制的,累加器也是强制的:
foldl' :: (a -> b -> a) -> a -> [b] -> a
foldl' f acc [] = acc
foldl' f acc (x : xs)
= acc' `seq` foldl' f acc' xs
where
acc' = f acc x
编译器要求iff
是严格的,例如(+)
在像Int
这样的严格数据类型上,累加器在每一步都被简化为Int
,而不是构建一个只在最后进行评估的thunk链。其他答案已经讨论了seq
的含义及其与pseq
的关系。但似乎有一些c关于seq
警告的确切含义
实际上,从技术上讲,代码< > SEQ B <代码>不能保证<代码> < <代码> >将在<代码> b/COD>之前进行评估。这可能看起来很麻烦:如果是这样的话,它怎么可能达到它的目的呢?让我们考虑乔恩在回答中给出的例子:
foldl' :: (a -> b -> a) -> a -> [b] -> a
foldl' f acc [] = acc
foldl' f acc (x : xs)
= acc' `seq` foldl' f acc' xs
where
acc' = f acc x
当然,我们关心的是在这里递归调用之前对acc'
进行评估。如果不是这样,那么foldl'
的全部目的就失去了!那么为什么不在这里使用pseq
呢?而且seq
真的就是这样吗
g a b c = (a `pseq` b) + c
foo (x + y)
f a (b + 1) c
acc' `seq` foldl' f acc' xs