我有这两个函数,foldl和foldr。在进行了一系列函数定义之后,我测试了两个替代方案,并且在函数调用和定义的两个链之间可以找到的唯一差异是在这两个函数之间,并且由于某种原因,调用foldr的函数需要更长的时间(使用大输入)
这是foldl:
SELECT
[Month] = DATEPART(mm, o.OrderDate),
c.EmailAddress,
Customers = CASE WHEN EXISTS (
SELECT 1
FROM dbo.Orders o2
WHERE o.CustomerID = o2.CustomerID AND o.orderDate < o2.orderDate
)
THEN 'Repeat'
ELSE 'New'
END
FROM
dbo.Customers c
JOIN dbo.Orders o
ON c.CustomerID = o.CustomerID
JOIN OrderDetails od
ON o.OrderID = od.OrderID
WHERE
o.OrderDate >= '2017-01-01'
AND o.OrderDate < '2017-02-01'
GROUP BY
DATEPART(mm, o.OrderDate),
c.EmailAddress,
od.productcode,
c.CustomerID;
这里是foldr:
(define (foldl op z ls)
(if (null? ls)
z
(foldl op (op z (car ls)) (cdr ls))))
我的问题是为什么与调用 foldl 的链相比,调用 foldr 的链,运行时间更长更长?
答案 0 :(得分:2)
foldl
的实现是尾递归的,因为foldl
是每次调用的最后一个函数。 foldr
的实现不是尾递归的,因为op
是每次调用的最后一件事。
好的,那是什么意思?
每次foldl
自我调用时,op
已经应用并返回一个值。编译器可以将其优化为等效循环。相反,当foldr
调用自身时,仍需要应用op
,因此在递归调用op
后,程序必须记住应用foldr
返回值。不幸的是,对foldr
的递归调用在op
应用于foldr
的下一个递归调用之前不能返回值,依此类推,直到列表结束。然后在列表的末尾,必须逐个应用op
的每个待处理应用程序。
记住待处理的op
的所有应用程序需要时间和内存空间。