【问题标题】:Improving an iterative function in scheme改进方案中的迭代函数
【发布时间】:2021-05-24 23:17:36
【问题描述】:

我经常在方案中编写迭代函数:它使编写递归过程变得更加简单。下面是一个尝试使用迭代过程对列表中的项目进行平方的示例:

(define square (lambda (x) (* x x)))
(define (square-list items)
  (define result nil) ; set result
  (define (iter items-remaining)
    (if (null? items-remaining)
        result
        (set! result (cons (car items-remaining) (iter (cdr items-remaining))))))
  (iter items))

(square-list '(1 2 3 4 5))
; (4 9 16 25)

我的主要问题是:

  • 有没有办法无需在内部过程之前先定义result 来执行此过程?我试图使迭代过程具有define (iter items-remaining answer) 的函数原型,但很难以这种方式实现它。

  • 如果没有,为什么不可能?

【问题讨论】:

    标签: scheme lisp sicp


    【解决方案1】:

    发布的代码不起作用;但即使经过修复以使其确实工作,这也不是一个惯用的 Scheme 解决方案。

    要使发布的代码工作:

    • nil 必须替换为'(),因为Scheme 不代表nil 的空列表
    • square 必须在 caritems-remaining 上调用
    • set! 应该通过添加平方数来修改result,而不是尝试添加递归调用的结果。这在这里根本不起作用,因为set! 返回未指定的值;但即使它确实工作,这也不会是尾递归(即,这不会是一个迭代过程)
    • result 的值必须返回,因为result 确实是一个累加器,所以必须先将其反转

    这是一个修正版:

    (define (square-list-0 items)
      (define result '()) ; set result
      (define (iter items-remaining)
        (cond ((null? items-remaining)
               result)
              (else
               (set! result (cons (square (car items-remaining))
                                  result))
               (iter (cdr items-remaining)))))
      (iter items)
      (reverse result))
    

    更好的解决方案不会使用突变,也不需要(define result '())

    (define (square-list-1 xs)
      (define (iter xs acc)
        (if (null? xs)
            (reverse acc)
            (iter (cdr xs) (cons (square (car xs)) acc))))
      (iter xs '()))
    

    这里将一个累加器acc 添加到iter 过程的lambda 列表中。在计算结果时,它们被转换为acc,这意味着在此过程结束时,acc 中的第一个数字基于xs 中的最后一个数字。所以,累加器在返回之前是反转的。

    另一种方法,可能是更惯用的解决方案,是使用一个名为let

    (define (square-list-2 xs)
      (let iter ((xs xs)
                 (acc '()))
        (if (null? xs)
            (reverse acc)
            (iter (cdr xs) (cons (square (car xs)) acc)))))
    

    这更简洁一些,它允许您在 iter 过程定义的开头将参数绑定到它们的参数。

    上述所有三个解决方案都定义了迭代过程,并且所有三个都给出了相同的结果:

    > (square-list-0 '(1 2 3 4 5))
    (1 4 9 16 25)
    > (square-list-1 '(1 2 3 4 5))
    (1 4 9 16 25)
    > (square-list-2 '(1 2 3 4 5))
    (1 4 9 16 25)
    

    当然,你可以使用map:

    > (map square '(1 2 3 4 5))
    (1 4 9 16 25)
    

    【讨论】:

    • 感谢您的出色回答。顺便说一句,我在顶部使用#lang sicp。
    猜你喜欢
    • 1970-01-01
    • 2010-11-30
    • 2014-05-07
    • 1970-01-01
    • 1970-01-01
    • 2013-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多