方案 - 为什么这个功能需要更长的时间才能运行

时间:2017-08-23 02:25:49

标签: scheme racket

我有这两个函数,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 的链,运行时间更长更长

1 个答案:

答案 0 :(得分:2)

foldl的实现是尾递归的,因为foldl是每次调用的最后一个函数。 foldr的实现不是尾递归的,因为op是每次调用的最后一件事。

好的,那是什么意思?

每次foldl自我调用时,op已经应用并返回一个值。编译器可以将其优化为等效循环。相反,当foldr调用自身时,仍需要应用op,因此在递归调用op后,程序必须记住应用foldr返回值。不幸的是,对foldr的递归调用在op应用于foldr的下一个递归调用之前不能返回值,依此类推,直到列表结束。然后在列表的末尾,必须逐个应用op的每个待处理应用程序。

记住待处理的op的所有应用程序需要时间和内存空间。