【问题标题】:Why the internal procedure?为什么是内部程序?
【发布时间】:2015-05-04 04:14:36
【问题描述】:
(define (decode bits tree)
  (define (decode-1 bits current-branch)
    (if (null? bits)
        '()
        (let ((next-branch (choose-branch (car bits) current-branch)))
          (if (leaf? next-branch)
              (cons (symbol-leaf next-branch)
                    (decode-1 (cdr bits) tree))
              (decode-1 (cdr bits) next-branch)))))
  (decode-1 bits tree))

如果我们对decodedecode-1 使用相同的参数,为什么我们还需要decode-1

【问题讨论】:

    标签: scheme procedure


    【解决方案1】:

    decode-1实际上是指decode参数tree(在(decode-1 (cdr bits) tree)中),而不仅仅是current-branch。所以它们不是“相同的论点”。 (从技术上讲,decode-1closure。)

    如果decode-1 没有引用tree(或任何外部变量,一般来说),那么可以,您可以直接递归到decode 而不需要内部过程。

    顺便说一句,“定义一个过程并调用它”的习语很常见,Scheme 提供了一个"named let" syntax 以便更好地编写它:

    (define (decode bits tree)
      (let decode-1 ((bits bits) (current-branch tree))
        (if (null? bits)
            '()
            (let ((next-branch (choose-branch (car bits) current-branch)))
              (if (leaf? next-branch)
                  (cons (symbol-leaf next-branch) (decode-1 (cdr bits) tree))
                  (decode-1 (cdr bits) next-branch))))))
    

    【讨论】:

      【解决方案2】:

      创建这样一个内部过程的一个好处是您可以简单地修改它的实现而不改变参数数量。例如,添加一个额外的参数作为过程的累加器,以便它可以是尾递归的。 decode-1 不是正确的尾递归,所以如果 bitstree 太大,那么实现可能会导致堆栈溢出。

      以下是转换 decode 尾递归调用的示例:

      (定义(解码位树) (define (decode-1 bits current-branch acc) (如果(空?位) (反向ACC) (let ((next-branch (choose-branch (car bits) current-branch))) (如果(叶?下一个分支) (decode-1 (cdr bits) tree (cons (symbol-leaf next-branch) acc)) (decode-1 (cdr bits) next-branch acc))))) (decode-1 bits tree '()))

      更新

      上面这段代码也可以用named-let来写。所以你想用内部定义来写它的情况将是以下情况。

      1. 如果内部程序有交叉引用,那么它可以是您可以选择的选项之一。这也可以通过letrec 完成,但嵌套级别会稍微深一些。所以在你的情况下,choose-branch 可以写在decode 过程中。
      2. 如果顶层过程包含带有define-syntax 的内部宏定义,那么您只能使用内部定义编写它。 R7RS 的let(rec)-syntax 创建了一个范围,但内部的define-syntaxdefine 应该被视为在同一个范围内。

      我不确定 R7RS 是否指定了如何扩展内部宏(例如,首先扩展 R6RS 等所有宏)。

      【讨论】:

      • 不,您不能“简单地消除内部定义”。正如我在回答中提到的,内部过程是一个闭包。事实上,即使你的尾递归定义仍然是一个闭包,所以如果不添加额外的 tree 参数,你仍然无法将其提升到顶级过程。
      • Opps,抱歉,我没有注意到 tree 参数。前两句应该去。
      • 你能解释一下尾递归版本的工作原理吗?看代码我似乎无法理解...如果位为空,为什么要反转 acc?
      • 我现在明白你为什么要反转 acc... 是否可以这样做,这样你就不必反转它了?
      • 您也可以使用append 代替cons 来避免reverse,但这会花费O(n^2),因为附加了O(n)。
      猜你喜欢
      • 1970-01-01
      • 2012-05-17
      • 2019-06-07
      • 2011-03-23
      • 1970-01-01
      • 2010-10-24
      • 1970-01-01
      • 2011-05-19
      • 2014-05-14
      相关资源
      最近更新 更多