【问题标题】:Converting a function with two recursive calls in scheme to make it tail-recursive在方案中转换具有两个递归调用的函数以使其尾递归
【发布时间】:2011-07-19 12:40:28
【问题描述】:

开始之前:是的,这是大学的家庭作业。在我被告知我懒惰和邪恶之前:这部分作业是转换我们已经拥有的两个函数,这个是第 6 个。

(define (flatten-list a-list)
  (cond ((null? a-list) '())
      ((list? (car a-list)) 
       (append (flatten-list (car a-list)) (flatten-list (cdr a-list))))
      (else (cons (car a-list) (flatten-list (cdr a-list))))))

您可以猜到,即使列表是嵌套的,该函数也会将列表展平。我对转换的具体问题出现在 (list? (car a-list)) 条件中,我在其中进行了两次递归调用。我已经做了斐波那契,我可以通过在尾递归上设置两个“累加器”来做到这一点。但是,我的头脑还没有受过这方面的训练,还不知道该怎么做。

如果我得到提示而不是结果,我将不胜感激。谢谢!

【问题讨论】:

    标签: functional-programming scheme tail-recursion


    【解决方案1】:

    这是我的解决方案:

    (define (flatten-iter a-list)
      (define (flat-do acc lst-interm lst)
        (cond 
          ((null? lst)
           (reverse acc))
          ((and (list? lst-interm) (not (null? lst-interm)))
           (flat-do acc (car lst-interm) (append (cdr lst-interm) lst)))
          ((not (list? lst-interm))
           (flat-do (cons lst-interm acc) empty lst))
          ((list? (car lst))
           (flat-do acc (car lst) (cdr lst)))
          (else
           (flat-do (cons (car lst) acc) empty (cdr lst)))))
      (flat-do empty empty a-list))
    
    (flatten-iter (list 1 (list 2 (list 3 4 (list 5 empty 6))) 7 8))
    => (1 2 3 4 5 6 7 8)
    

    尾递归函数要求它们永不返回,因此您不能使用堆栈来存储程序的状态。相反,您使用函数参数在函数调用之间传递状态。因此,我们需要确定如何维护该状态。因为我们函数的结果是list?,所以增长一个empty列表是有意义的;为此,我们使用acc。你可以在上面的else 分支中看到它是如何工作的。但是我们应该能够处理嵌套列表。当我们更深入时,我们应该保留嵌套列表的其余元素以供进一步处理。样本列表:(list 1 (list 2 3) 4 5)

    直到(list 2 3),我们已经将1 添加到累加器。由于我们不能使用堆栈,我们需要一些其他地方来存储列表的其余元素。而这个地方就是lst参数,里面包含了要展平的原始列表的元素。我们可以只使用appendlst 到其余元素(cdr (list 2 3))(即(list 3)),然后继续处理我们在展平时偶然发现的列表头部,即。 e. (car (list 2 3)) 这只是2。现在,(and (list? lst-interm) (not (null? lst-interm))) 成功了,因为 flat-do 是这样调用的:

    (flat-do (list 1) (list 2 3) (list 4 5))
    

    条件触发此代码:

    (flat-do (list 1) (car (list 2 3)) (append (cdr (list 2 3)) (list 4 5)))
    

    flat-do 再次这样调用:(flat-do (list 1) 2 (list 3 4 5))

    条件(not (list? 2)) 现在成功并且代码(flat-do (cons 2 1) empty (list 3 4 5)) 被评估。

    其余的处理通过else 分支完成,直到lstnull? 并且reverseacc 上执行。然后函数返回反转的累加器。

    【讨论】:

    • 希望我能够感谢您的快速回复,Yasir。读完之后,我确实明白它在做什么(这很重要),但如果你能提示我你所遵循的思想过程,我将不胜感激。对不起,如果我要求太多!非常感谢到目前为止的帮助。
    • Mamsaac:我现在要去吃披萨了。如果你不介意,让我稍后再详细解释一下:-)
    • Buen provecho :) (享受美食)
    • 当你在等待 Yasir 吃完他的披萨时:有一种通用技术叫做“连续传递风格”(CPS),它可以转换任何一段代码,以便 all i> 呼叫处于尾部位置。 Matt 最近可能有一篇关于 CPS 转换的不错的博文,您可能想看看。
    • Mamsaac:我更新了答案。我同意@John,CPS 是一种很好的技术,值得研究。
    猜你喜欢
    • 1970-01-01
    • 2019-09-14
    • 1970-01-01
    • 1970-01-01
    • 2016-01-06
    • 1970-01-01
    • 1970-01-01
    • 2018-10-24
    • 1970-01-01
    相关资源
    最近更新 更多