Letrec 有什么好处?

2024-06-22

在阅读《老练的阴谋家》时,我开始了解letrec。我理解它的作用(可以用 Y-Combinator 复制),但这本书正在使用它来代替已经重复出现的内容defined 函数对保持静态的参数进行操作。

使用旧函数的示例defined 函数本身重复出现(没什么特别的):

(define (substitute new old l)
  (cond
    ((null? l) '())
    ((eq? (car l) old)
      (cons new (substitute new old (cdr l))))
    (else
      (cons (car l) (substitute new old (cdr l))))))

现在举一个相同功能但使用的示例letrec:

(define (substitute new old l)
  (letrec
    ((replace
      (lambda (l)
        (cond
          ((null? l) '())
          ((eq? (car l) old)
           (cons new (replace (cdr l))))
          (else
           (cons (car l) (replace (cdr l))))))))
(replace lat)))

除了稍微长一点和更难阅读之外,我不知道为什么他们要重写书中的函数以使用 letrec。当以这种方式重复静态变量时,速度是否会提高,因为您不继续传递它?

对于参数保持静态但一个参数减少的函数(例如重复出现列表的元素),这是标准做法吗?

来自更有经验的策划者/LISPers 的一些意见将会有所帮助!


所以你有一些涵盖可读性问题的答案,这应该没问题。但有一个问题尚不清楚,即是否存在性能问题。粗略地看,似乎letrec版本,如命名的-let一个(实际上是相同的)应该更快,因为循环中传递的参数较少。然而,实际上,编译器可以在你背后进行各种优化,例如找出普通版本中的循环不变地传递前两个参数,并将其转换为带有第一个参数的单参数循环。这里不是向您显示特定系统上的数字,而是一个 PLT 模块,您可以运行它来对四个不同版本进行计时,并且您可以轻松添加更多版本来尝试其他变体。简短的总结是,在我的机器上,名为 -let版本稍快一些,并且使其尾递归具有更大的总体优势。

#lang scheme

;; original version
(define (substitute1 new old l)
  (cond [(null? l) '()]
        [(eq? (car l) old) (cons new (substitute1 new old (cdr l)))]
        [else (cons (car l) (substitute1 new old (cdr l)))]))

;; letrec version (implicitly through a named-let)
(define (substitute2 new old l)
  (let loop ([l l])
    (cond [(null? l) '()]
          [(eq? (car l) old) (cons new (loop (cdr l)))]
          [else (cons (car l) (loop (cdr l)))])))

;; making the code a little more compact
(define (substitute3 new old l)
  (let loop ([l l])
    (if (null? l)
      '()
      (cons (let ([fst (car l)]) (if (eq? fst old) new fst))
            (loop (cdr l))))))

;; a tail recursive version
(define (substitute4 new old l)
  (let loop ([l l] [r '()])
    (if (null? l)
      (reverse r)
      (loop (cdr l)
            (cons (let ([fst (car l)]) (if (eq? fst old) new fst)) r)))))

;; tests and timings

(define (rand-list n)
  (if (zero? n) '() (cons (random 10) (rand-list (sub1 n)))))

(for ([i (in-range 5)])
  (define l   (rand-list 10000000))
  (define new (random 10))
  (define old (random 10))
  (define-syntax-rule (run fun)
    (begin (printf "~a: " 'fun)
           (collect-garbage)
           (time (fun new old l))))
  ;; don't time the first one, since it allocates a new list to use later
  (define new-list (substitute1 new old l))
  (unless (and (equal? (run substitute1) new-list)
               (equal? (run substitute2) new-list)
               (equal? (run substitute3) new-list)
               (equal? (run substitute4) new-list))
    (error "poof"))
  (newline))
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Letrec 有什么好处? 的相关文章

  • “映射”是否一定会产生额外的嵌套级别?

    是否使用嵌套map自动创建另一层嵌套 这是我使用的一个基本示例 One level map lambda x1 Hi 1 Two levels map lambda x1 map lambda x2 Hi 1 1 Three levels
  • 内置方案以检查列表包含情况

    在Python中 我可以执行 x in list 来查看列表是否包含x 方案中是否有等效的内置功能可以做到这一点 The R5RS http schemers org Documents Standards R5RS HTML r5rs Z
  • 为什么在 Scala 中函数类型需要以单独的参数组传递到函数中

    我是 scala 新手 我用两种方式编写了相同的代码 但我对两种方式有点困惑 在第二种方式中 f 的参数类型是自动派生的 但在 type1 中 scala 编译器无法执行相同的操作 我只是想了解这背后的想法是什么 Type1 给出编译错误
  • 设计 React Hooks 可防止 React-hooks/exhaustive-deps 警告

    我正在设计一个钩子 仅当钩子依赖项发生变化时才获取数据 它按预期工作但我收到了 linter 警告 React Hook useEffect was passed a dependency list that is not an array
  • 是否有适用于 Haskell 或 Scala 等函数式语言的 LL 解析器生成器?

    我注意到明显缺乏用函数式语言创建解析器的 LL 解析器 我一直在寻找但没有成功的理想发现是为 ANTLR 风格的 LL 语法生成 Haskell 解析器 语法的模小数重新格式化 并且令我惊讶的是 每个最后一个解析器生成器都具有函数我发现的语
  • 如何在 Common Lisp 中进行模式匹配

    我不知道 Common Lisp 是否存在模式匹配函数 但我必须制作自己的函数 我对Lisp一无所知 有人可以对学习 Lisp 以及最重要的是如何在 Lisp 中进行模式匹配进行提示吗 我必须传递一个模式和一个事实 并判断它们是否匹配 一个
  • 在 Vavr 中结合任一者?

    我有几个Vavr https www vavr io Either https www vavr io vavr docs either的 我想调用一个函数Right每个 Either 的值 例如 Either
  • Letrec 有什么好处?

    在阅读 老练的阴谋家 时 我开始了解letrec 我理解它的作用 可以用 Y Combinator 复制 但这本书正在使用它来代替已经重复出现的内容defined 函数对保持静态的参数进行操作 使用旧函数的示例defined 函数本身重复出
  • Common Lisp 中的重复元素 [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我尝试创建一个函数two argum
  • 了解函数类型

    我在尝试理解 Haskell 如何确定函数类型时感到有点困惑 这是一个例子 boolFcn x y x 3 y 4 当我检查上述函数的类型时 它给出了结果 Num a1 Num a Eq a1 Eq a gt a gt a1 gt Bool
  • 对参数进行排序以利用柯里化

    我最近两次重构代码以更改参数的顺序 因为代码太多 黑客喜欢flip or x gt foo bar x 42正在发生 在设计函数签名时 哪些原则可以帮助我充分利用柯里化 对于轻松支持柯里化和部分应用的语言 有一系列令人信服的论点 最初来自
  • 查找列表中元素的索引

    我需要获取方案列表中元素的索引 例如 2 2 3 4 5 4 2 3 4 5 2 有人可以帮忙吗 像这样的东西 define list index lambda e lst if null lst 1 if eq car lst e 0 i
  • F# 中灵活类型注释的用途是什么?

    我正在学习 F 我不明白灵活类型的目的 或者更好的是 我无法理解这样写的区别 set TextOfControl c Control s c Text lt s 并写下 set TextOfControl c T when T gt Con
  • 程序解释期间高效的增量哈希计算

    我想写一个递归记忆Scheme解释器 在求值过程中的任何时刻 解释器都应该能够检测到它何时接收到之前见过的一对表达式和环境作为参数 简单记忆eval and apply效率低下 每次调用时都需要在哈希表中查找参数eval apply 这需要
  • JavaScript 中 Scala View 的等效项

    在斯卡拉中 view允许防止创建全新的集合 例如在Scala中 视图 有什么作用 https stackoverflow com questions 6799648 in scala what does view do JavaScript
  • 为什么(require (for-syntax 'm))在DrRacket中执行了3次required模块中的表达式?

    我不知道如何解释 DrRacket 交互窗口中的以下行为 我认为输出应该只有一个 hello 因为在这种情况下模块 m 应该实例化一次 但实际上 hello 被打印了3次 gt module m racket printf hello n
  • 当我尝试在 Windows 7 中克隆 HN 时出现错误:无法识别“rm”

    我已将 Arc 3 1 和 Racket 下载到我的 Windows 7 计算机上 我已经按照指示解决了许多错误http www arclanguage org item id 12397 http www arclanguage org
  • 结合记忆和尾递归

    是否有可能以某种方式将记忆和尾递归结合起来 我目前正在学习 F 理解这两个概念 但似乎无法将它们结合起来 假设我有以下内容memoize函数 来自现实世界的函数式编程 http www manning com petricek let me
  • 不带关键字参数的 Python 库函数

    当我尝试对 python 中的问题应用更实用的方法时 就出现了这个问题 我想做的只是对一系列数字进行平方 没什么大不了的 from operator import pow from functools import partial squa
  • 在 JavaScript 中使用 Array.map 删除元素

    我想使用以下方法过滤一系列项目map 功能 这是一个代码片段 var filteredItems items map function item if some condition return item 问题是过滤掉的项目仍然使用数组中的

随机推荐