我是一位Haskell新手,他在理解seq
时遇到了问题,我在下面的示例中对此进行了总结。了解Haskell seq
下面是相同(傻)功能的2个实现。该函数采用正整数n并返回元组(n,n)
。答案是通过使用具有元组累加器的辅助函数从(0,0)
开始计数而产生的。为了避免构建一个大型的thunk,我使用seq来使得累加器严格。
在testSeq1
元组的内容是严格的,而在testSeq2
元组本身是严格的。我认为这两个实现将具有相同的性能。但实际上testSeq1
的'使用总内存'仅为1MB,而testSeq2
(当使用n = 1000000进行测试时)为187MB。
testSeq2
有什么问题?
testSeq1 :: Int -> (Int,Int)
testSeq1 n = impl n (0,0) where
impl 0 (a,b) = (a,b)
impl i (a,b) = seq a $ seq b $ impl (i-1) (a+1, b+1)
testSeq2 :: Int -> (Int,Int)
testSeq2 n = impl n (0,0) where
impl 0 acc = acc
impl i acc = seq acc $ impl (i-1) ((fst acc)+1, (snd acc)+1)
对于元组构造函数来说,这是一种特殊的处理方法,还是你说对任何构造函数的参数都不会发生评估?假设我创建了自己的元组数据类型,而不是使用内置的元组,结果会是一样的吗? – BillyBadBoy
这不是特殊的元组;发生这种情况是因为Haskell是懒惰的,只会根据需要进行评估。你可以用'Just undefined'或者你自己的数据类型做类似的例子来测试。 – Tordek
这对我来说很震惊。我认为seq的全部重点是能够覆盖Haskell的默认“懒惰”并迫使它完全评估一个表达式。这解释了为什么我尝试使用foldl'没有按照我的预期工作 - 我在我的累加器函数中使用了元组构造函数。 – BillyBadBoy