【问题标题】:Racket is a define form in a procedure run multiple times?球拍是一个程序运行多次的定义形式?
【发布时间】:2017-02-12 01:48:07
【问题描述】:

在 Racket 中,可以在另一个过程中定义一个过程和常量,例如:

(define (a a-list)
  (define something 10)
  (display a-list)
  (cond
    [(empty? a-list) (display "done")]
    [else
      (display a-list) (newline)
      (a (rest a-list))]))

打印列表,然后取出第一个元素,然后再次打印列表,以此类推,直到列表为空。

在示例过程中,值something 被定义为10,无论出于何种原因。当过程递归时,Racket 是否再次定义该值,或者它是否足够聪明地注意到这个定义没有改变?

它也可能在先前调用的过程的堆栈空间上,如果这没有优化,并且每个递归步骤会消耗更多的内存,这取决于 something 的定义。例如,假设列表中有一百万个数字,但从不改变数字。显然,如果该定义中的内容依赖于作为参数给出的列表,则不能如此简单地对其进行优化。

如果这没有优化,那么在递归函数中不要有太多的define 形式是一个好主意,还是只有那些消耗很少空间的形式?将它们放在过程之外将使它们可以从其他过程中访问,即使只有一个过程曾经使用它们。

【问题讨论】:

    标签: recursion optimization racket


    【解决方案1】:

    每次调用过程时都会评估define 表达式。考虑这个例子:

    (define (test1)
      (define x (begin (display "Hello from test1 ") 10))
      x)
    
    (test1)
    => Hello from test1 10
    (test1)
    => Hello from test1 10
    (test1)
    => Hello from test1 10
    

    如果你希望一个过程中的值只被评估一次(当过程本身被创建时),你可以把它放在过程之外的范围内,它仍然可以访问,因为 lambda 定义了一个 @ 987654321@ 在其词法环境中:

    (define test2
      (let ((x (begin (display "Hello from test2") 20)))
        (lambda () x)))
    
    => Hello from test2
    (test2)
    => 20
    (test2)
    => 20
    (test2)
    => 20
    

    【讨论】:

    • 我不明白第一个例子。当然,当我调用该过程时,它会打印出x 的值。我很想知道,是否在幕后进行了优化以避免每次都必须定义 x 的值,因为每次都是一样的。不过,关于如何使用闭包的建议似乎很棒,以防过程中的不变定义没有这种优化(也许一般来说也是如此?)。也许我应该问类似 “Racket 的 JIT 是否优化了过程中不变值的定义?”
    • 你不明白 - 是的,它会打印 x,但它也每次都会打印 display 部分中的值。每次都必须评估定义,这是不可优化的。
    • 啊,我明白了!谢谢!我不明白的是,为什么不管它是什么定义,它都是完全不可优化的。我的意思是(define a 10) 和其他不变的价值观定义。所以我想使用您在第二个示例中使用的方式是一个好习惯,以避免多次评估相同的不变事物?
    • 绑定到define中的值的表达式可能会在每次执行过程时发生变化,或者变量本身可以在过程中被修改或重新分配,因此编译器别无选择但每次都要评估它。所以是的,如果你需要一个表达式只被计算一次,按照我们在第二个例子中所做的那样去做。
    • 您是否知道一个网站或其他来源,其中列出了 Racket 中的最佳实践之类的内容,其中包括您的第二个示例之类的内容,以及您给出的解释?我想从一开始就以“正确的方式”学习球拍。
    猜你喜欢
    • 2019-05-03
    • 2015-11-24
    • 1970-01-01
    • 2017-02-03
    • 1970-01-01
    • 2014-11-22
    • 1970-01-01
    • 2019-10-29
    • 1970-01-01
    相关资源
    最近更新 更多