【问题标题】:Advantage of using an inner loop in functions Racket在函数 Racket 中使用内循环的优势
【发布时间】:2016-12-25 17:01:38
【问题描述】:

Expanded form of fold in Racket 中定义 foldl ,第一个函数比第二个函数有什么优势:

(define (oldfoldl f init l)
  (let loop ([init init] [l l])   
    (if (null? l)  init
        (loop (f (car l) init) (cdr l)))))

(define (myfoldl f init l)
  (if (null? l) init
      (myfoldl f (f (car l) init) (cdr l)) ))

换句话说,使用内部子函数有什么好处吗? 两者都给出相同的结果:

(oldfoldl + 0 '(1 2 3 4))
(myfoldl + 0 '(1 2 3 4))

输出:

10
10

编辑:使用 define 而不是 let 的第三个版本如下。会不会不一样:

(define (myfoldl2 f init l)
  (define (loop [init init] [l l])   
    (if (null? l)  init
        (loop (f (car l) init) (cdr l)) ))
  (loop init l) ) 

【问题讨论】:

  • 不,第二次可能会更快,因为f 不是“免费的”(但可能不会在优化之后)。两者都有 98% 的机会生成相同的优化代码(对于可调试的代码,这将完全不同)。
  • 事后我在编辑中添加了另一个版本。
  • @mso:这可能会或可能不会生成相同的代码。一个好的编译器应该很容易内联它。编辑:define 版本扩展为letrec,命名为let 也是如此,所以即使是一个糟糕的编译器也应该能够做到。

标签: scheme racket


【解决方案1】:

对于您描述的简化定义,这两种形式实际上是完全等效的。唯一的区别是使用直接递归的方法在每次迭代时通过finitl,而使用命名let 的方法只通过initl。但是,我想这种差异完全可以忽略不计。

但是,该简化版本不是答案顶部提到的实际 foldl 函数,如下所示:

(define foldl
  (case-lambda
    [(f init l)
     (check-fold 'foldl f init l null)
     (let loop ([init init] [l l])
       (if (null? l) init (loop (f (car l) init) (cdr l))))]
    [(f init l . ls)
     (check-fold 'foldl f init l ls)
     (let loop ([init init] [ls (cons l ls)])
       (if (pair? (car ls)) ; `check-fold' ensures all lists have equal length
           (loop (apply f (mapadd car ls init)) (map cdr ls))
           init))]))

这使用case-lambda,它对参数计数执行调度,以及一个名为check-fold的内部函数,它执行参数类型检查。使用命名 let 而不是递归调用 foldl 可以避免在每次迭代时执行调度和运行时检查,这会产生更大的开销。

【讨论】:

    【解决方案2】:

    是的。通过保持不会因连续递归而改变的变量,您的眼睛会停留在您正在查看的特定行中重要的变量上。

    使用命名let 只是定义过程然后使用它的好方法,但变量和值在顶部。这使得更容易看到它是如何初始化的。

    在您的示例中,所有示例对我来说都不错,甚至是第一个在连续递归中传递不变变量时,但对于更复杂的过程,删除不变绑定和创建辅助过程有助于使其更容易理解。

    闭包/lambdas 应该相当便宜,因此在大多数情况下,将表达式包装在 let/procedure 中不会对速度产生太大影响。

    语言内部通常看起来有点神秘,所以我很惊讶实现如此简单。我敢打赌,真正的工作是在球拍优化器中,这也有利于我们的用户代码。这三个版本在字节码中可能无法区分。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-10
      • 1970-01-01
      • 1970-01-01
      • 2011-03-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多