- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
在阅读“The Seasoned Schemer”时,我开始了解letrec
。我了解它的作用(可以用 Y-Combinator 复制),但本书使用它来代替对已经define
d 函数的重复操作,该函数对保持静态的参数进行操作。
使用 define
d 函数重复自身的旧函数示例(没什么特别的):
(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。以这种方式重复静态变量时是否会提高速度,因为您不会一直传递它??
这种标准做法是否适用于参数保持静态但有一个参数减少的函数(例如在列表的元素中重复出现)?
更有经验的 Schemers/LISPers 的一些意见会有所帮助!
最佳答案
因此,您有一些涵盖可读性问题的答案,应该没问题。但一个不清楚的问题是是否存在任何性能问题。粗略地看,letrec
版本,如命名-let
版本(实际上是相同的)似乎应该更快,因为要传递的参数更少在循环。然而,在实践中,编译器可以在你背后进行各种优化,比如弄清楚普通版本中的循环不改变地传递前两个参数,然后将它变成一个带有第一个参数的单参数循环。这里没有显示特定系统上的数字,而是一个 PLT 模块,您可以运行它来计时四个不同的版本,并且您可以轻松添加更多版本以尝试其他变体。简短的总结是,在我的机器上,named-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))
关于functional-programming - letrec 有什么好处?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2060744/
两者 letrec和 letrec*在 R6RS 中,但有 only letrec在 Racket 中,没有 letrec* .这些之间有什么区别? 最佳答案 总之 Racket letrec和 R6
Kent Dybvig 在 The Scheme Programming Language for letrec 和 letrec* 中给出的两个示例是: (letrec ([sum (lambda
有人告诉我,以下表达式旨在计算为 0,但许多 Scheme 实现将其计算为 1: (let ((cont #f)) (letrec ((x (call-with-current-continuat
我正在尝试使用match-lambda模式匹配对letrec的调用。在我看来,这种模式: (match-lambda (`(letrec ((, ,) . (, ,)) , . ,) `()
在阅读“The Seasoned Schemer”时,我开始了解letrec。我了解它的作用(可以用 Y-Combinator 复制),但本书使用它来代替对已经defined 函数的重复操作,该函数对
怎么可以letrec无需使用 set! 即可实现? 在我看来 set!是一种命令式编程结构,如果使用它,就会失去函数式编程的好处。 最佳答案 我知道通常我们要求复制内容,但您的问题没有简短的答案。 h
我正在用 Scheme (DrRacket Pretty Big) 编写一个愚蠢的 letrec: (letrec ((is-creative? (lambda (writing)
R5RS 为库形式的语法提供了建议的宏定义: http://schemers.org/Documents/Standards/R5RS/HTML/r5rs-Z-H-10.html#%_sec_7.3
Core Erlang 的 letrec 有什么用? Richard Carlsson 在 "Introduction to Core Erlang" 中写道: Furthermore letrec
这个问题已经有答案了: Confused by the difference between let and let* in Scheme (2 个回答) 已关闭10 年前。 let、let* 和 l
我只是在玩一个 NFA 来识别字符串。我有一个宏,它创建一个函数,该函数消耗输入并将其余部分传递给其他一些函数。因为我的 NFA 图中可能存在循环,所以我使用 letrec 将整个图组合在一起。这是一
我有几本关于 Scheme 的书,其中一些提到了名为 let 和 letrec 的书,但实际上没有一本会给出令人信服的例子(我的意思是,我何时以及为什么会使用一个而不是另一个)。 有没有letrec/
我无法理解维基百科上给出的 HM 系统的 letrec 定义,这里:https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system#R
我是计划的初学者。我曾经在使用 letrec 和使用 lambda 绑定(bind)时执行过类似的程序。 (define (drop l n) (letrec ((iter (lambda(ls
考虑在方法主体中定义为 lambda 表达式并分配给变量的阶乘函数: Func factfail = n => { if (n == 0) return 1; else
我不明白两者之间有什么区别(对于人为的例子感到抱歉): (define average (lambda (elems) (define length (lambda (xs)
我是一名优秀的程序员,十分优秀!