在方案中使用两个递归调用转换函数以使其呈尾递归

时间:2011-03-21 08:50:35

标签: functional-programming scheme tail-recursion

在我开始之前:是的,这是大学的家庭作业。在我被告知我是懒惰和邪恶之前:这部分功课是转换我们已经拥有的两个功能,这个是第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))条件中,我正在进行两次递归调用。我已经做了斐波纳契,我可以通过在尾递归上只有两个“累加器”来做。但是,我还没有接受过这方面的训练,也不知道应该怎么做。

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

1 个答案:

答案 0 :(得分:6)

这是我的解决方案:

(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)

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

(list 2 3)之前我们已将1添加到累加器。由于我们不能使用堆栈,我们需要一些其他地方来存储列表的其余元素。这个地方是lst参数,其中包含要展平的原始列表的元素。我们可以append lst (cdr (list 2 3))(list 3)的其余元素2,继续我们在展平时偶然发现的列表头部,i。即(车(清单2 3))只是(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

(not (list? 2))再次以这种方式调用:( flat-do(list 1)2(list 3 4 5))

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

其他处理是使用lst分支完成的,直到null?reverseacc在{{1}}上执行。然后函数返回反向累加器。