CS61A_project_scheme

发布时间 2023-04-26 08:07:20作者: 哎呦_不想学习哟~

 

(define (list-change total denoms)
(define (cons-all num ls)
 (let ((num-val num))
  (cond
    ((null? ls) nil)
    (else
     (cons (cons num (car ls))
           (cons-all num (cdr ls)))))))
  (if (= total 0)
      (list nil)
      (if (null? denoms)
          nil
         (if (>= total (car denoms))
              (append
               (cons-all (car denoms) (list-change (- total (car denoms)) denoms))
               (list-change total (cdr denoms))
               )
              (list-change total (cdr denoms))
          )
      )
  )
)

这段程序实现了一个函数 `list-change`,该函数接受两个参数 `total` 和 `denoms`,其中 `total` 是需要找零的总金额,`denoms` 是一个递减的硬币面额列表,表示可用的硬币面额。

函数的作用是计算出总金额为 `total` 的找零方案,即用 `denoms` 列表中的硬币组合成 `total`,并返回所有可能的组合方式。

函数的实现采用了递归的方式。首先,如果 `total` 等于 0,说明已经找到了一种组合方式,直接返回一个包含空列表的列表,表示找零方案为空。如果 `denoms` 列表为空,说明无法组成总金额为 `total` 的找零方案,返回空列表。

否则,将硬币面额列表 `denoms` 分成两个部分:第一个元素 `car denoms`,和剩余的元素列表 `cdr denoms`。如果 `total` 大于等于第一个元素 `car denoms`,说明可以使用第一个元素作为硬币面额,将 `total` 减去第一个元素,然后递归调用 `list-change` 函数,计算剩余金额的找零方案。将递归返回的结果与当前硬币面额组成一组解,然后递归调用 `list-change` 函数,计算使用剩余硬币面额的找零方案。将这两个结果合并起来,得到所有可能的解。

如果 `total` 小于第一个元素 `car denoms`,则不使用当前硬币面额,直接递归调用 `list-change` 函数计算剩余硬币面额的找零方案。

 

好的,我来逐行分析这段程序:

```
(define (list-change total denoms)
  ; 定义辅助函数 cons-all,用于在列表中的每个元素前面添加一个数值
  (define (cons-all num ls)
    (let ((num-val num))
      (cond
        ; 如果列表为空,则返回空列表
        ((null? ls) nil)
        ; 否则,将当前数值与列表中的第一个元素组成一组,递归调用 cons-all 处理剩余元素
        (else
         (cons (cons num (car ls))
               (cons-all num (cdr ls)))))))
  ; 如果总金额为 0,则找零方案为空,返回包含空列表的列表
  (if (= total 0)
      (list nil)
      ; 如果可用硬币面额列表为空,则无法找零,返回空列表
      (if (null? denoms)
          nil
          ; 如果总金额大于等于当前硬币面额,则使用当前硬币面额
          (if (>= total (car denoms))
              ; 递归调用 list-change 函数处理剩余金额,将递归返回的结果与当前硬币面额组成一组解
              ; 然后递归调用 list-change 函数计算使用剩余硬币面额的找零方案
              (append
               (cons-all (car denoms) (list-change (- total (car denoms)) denoms))
               (list-change total (cdr denoms)))
              ; 如果总金额小于当前硬币面额,则不使用当前硬币面额,直接递归处理剩余硬币面额的找零方案
              (list-change total (cdr denoms)))))))
```

第一行定义了一个名为 `list-change` 的函数,它接受两个参数 `total` 和 `denoms`,分别表示总金额和可用的硬币面额。

接下来定义了一个辅助函数 `cons-all`,该函数接受两个参数 `num` 和 `ls`,将数值 `num` 与列表 `ls` 中的每个元素组成一组,返回一个新列表。

在主函数中,首先判断总金额 `total` 是否为 0,如果是,则说明已经找到了一种解法,直接返回一个包含空列表的列表。

如果总金额不为 0,则判断可用硬币面额列表 `denoms` 是否为空。如果为空,则说明无法找零,返回空列表。

如果可用硬币面额列表不为空,则将其分为两部分:第一个元素 `car denoms`,和剩余的元素列表 `cdr denoms`。

如果总金额大于等于第一个元素 `car denoms`,说明可以使用当前硬币面额,将 `total` 减去当前硬币面额,然后递归调用 `list-change` 函数,计算剩余金额的找零方案。将递归返回的结果与当前硬币面额组成一

`(list-change 10 '(25 10 5 1))` 的返回值是一个列表,其中包含所有可能的找零方案。具体来说,它将使用所提供的面值列表 `(25 10 5 1)`,找到所有可能的组合,以便找出总面值为 10 的组合。

运行过程如下:

首先检查总面值是否为 0。由于它不是 0,程序将进入第二个if语句。

由于面值列表`(25 10 5 1)`不是空的,程序将检查总面值是否大于或等于面值列表中的第一个面值 25。由于总面值为 10 小于 25,程序将进入第二个if语句的else分支,即忽略第一个面值 25。

程序将递归调用`list-change`函数,传递总面值 10 和面值列表 `(10 5 1)`,这将在下一个步骤中用于计算可能的找零方案。

现在总面值是 10,面值列表是 `(10 5 1)`。程序将检查总面值是否大于或等于面值列表中的第一个面值 10。由于它们相等,程序将调用`cons-all`函数,将面值 10 添加到可能的找零方案中,同时递归调用`list-change`函数,传递总面值为 0 和面值列表 `(10 5 1)`。

由于总面值为 0,程序将返回`(list nil)`,表示找到了一个可能的找零方案。

现在,`cons-all`函数将为面值列表中的下一个面值 5 重复上述过程,然后为面值列表中的最后一个面值 1 重复这个过程。最终,程序将返回一个包含所有可能找零方案的列表:`((10) (5 5) (5 1 1 1 1 1) (1 1 1 1 1 1 1 1 1 1))`。