你的函数应该写成
undo_prefix_sum' :: (Num a) => [a] -> [a]
undo_prefix_sum' xs = init $ snd $
foldr (\x ~(y, ys) -> (x, (y-x):ys))
(undefined, [])
(0:xs)
您的tot
代码中的 不是“总数”,它只是输入中的下一个值 after x
。所以这里需要名称中的一些对称性(命名很重要)。last xs
因为调用,所以从不计算位,因此init
可以是undefined
,以避免给人以错误的印象。
由于它所做的只是计算每个元素与其前身(或0
第一个元素)之间的差异,我们不妨清楚这一点:
foo xs = [y-x | i <- [0..length xs-1], let y=xs!!i ; x = (0:xs)!!i ]
这当然是二次的,它计算出length
哪个是大禁忌;但至少它很简单,所以它很容易修复:
bar xs = [y-x | x <- 0:xs | y <- xs]
= map (uncurry (-)) $ zip xs (0:xs)
= zipWith (-) xs (0:xs)
正如评论中所述,您已经看到了。因此,它在计算上实际上等同于您的代码,这与另一个答案中的代码不同,后者通过实质上不同的、本质上是 二次的(并且不必要的)计算来计算相同的结果。
如果您真的想将zipWith (-)
过程表达为展开,可以将其完成为
baz xs = [y-x | (x:y:_) <- tails (0:xs)]
因为tails ~= iterate tail
, 和迭代和展开本质上是一回事(每个都可以表示为另一个,有一些可能需要的预处理和/或后处理步骤)。
最后一件事。你的代码确实有问题。这是不必要的严格:
> take 2 $ undo_prefix_sum $ [1,2] ++ undefined
*** Exception: Prelude.undefined
> take 2 $ undo_prefix_sum' $ [1,2] ++ undefined
[1,1]
foo
上面也同样严格。此答案中的所有其他版本(以及其他答案中的代码)都成功且正确地计算了结果。除了变量重命名之外,这里和您的代码之间的唯一区别undo_prefix_sum'
是一个非常小的区别。
你能发现吗?
除此之外,无论您的代码名义上使用foldr
or unfoldr
,重要的是它是否足够懒惰。foldr
具有惰性组合功能的代码同样能够编写. 像这样sum
的变质必然是严格的。要将您的函数编写为适当的变形,使其仍然应该是线性的,我们需要通过压缩输入来预处理输入,tail
因为我们一次需要两个头部元素。对此进行编码的另一种方法是使用paramorphism,以便我们可以访问输入的当前尾部及其当前元素。并且通过折叠(是的,就像正在做的那样)可以很容易地模拟拟态。tails
baz