CS61A_project_scheme

 

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

 

题目描述:

题解

(define (let-to-lambda expr)
  (cond ((atom? expr)
         expr
         )
        ((quoted? expr)
         expr
         )
        ((or (lambda? expr)
             (define? expr))
         (let ((form   (car expr))
               (params (cadr expr))
               (body   (cddr expr)))
           (cons form (cons params (map let-to-lambda body)))
           ))
        ((let? expr)
         (let ((values (cadr expr))
               (body   (cddr expr)))
           (define tmp (zip values))
           (append (cons (cons 'lambda (cons (car tmp) (map let-to-lambda body))) nil) (map let-to-lambda (cadr tmp)))
           ))
        (else
         (cons (car expr) (map let-to-lambda (cdr expr)))
         )))

 

这段代码是一个Scheme程序,它实现了一个将let表达式转换为lambda表达式的函数。具体而言,它将使用let表达式定义的局部变量绑定转换为使用lambda表达式的形式。

该函数采用一个参数"expr",表示要转换的表达式。它使用cond语句对表达式进行分类,并根据表达式的类型采取不同的转换方式:

- 如果表达式是一个原子,则不进行转换,直接返回该表达式。
- 如果表达式是一个引用,则不进行转换,直接返回该表达式。
- 如果表达式是一个lambda表达式或一个定义表达式,则将其转换为一个lambda表达式,并递归地对其参数和主体应用let-to-lambda函数。具体来说,该函数将取出该lambda表达式的参数和主体,将它们包装在一个新的lambda表达式中并返回。
- 如果表达式是一个let表达式,则将其转换为一个lambda表达式。具体来说,该函数将取出let绑定的变量和值,将它们打包为一个临时变量(称为tmp),然后构造一个lambda表达式,其中参数是tmp的第一个元素,主体是let表达式中的主体,再递归地对tmp的第二个元素(也就是未包含在主体中的部分)应用let-to-lambda函数。最后,将新构造的lambda表达式放在一个cons中,该cons的第一个元素是'lambda,第二个元素是包含新构造的lambda表达式和未包含在主体中的部分的列表。
- 否则,将表达式的car应用到其cdr中的每个元素上,并递归地对其应用let-to-lambda函数。最后,将所有的结果放在一个cons中,其第一个元素是表达式的car,第二个元素是对cdr中所有元素应用let-to-lambda函数的结果列表。

总之,这个函数实现了一种将let表达式转换为lambda表达式的方法,这样就可以避免使用let表达式的缺点,并使代码更加简洁和可读。

在Scheme中,`cadr`和`cddr`都是基本的列表操作函数,它们用于访问和操作列表中的元素。具体而言,它们的作用如下:

- `cadr`函数用于获取列表的第二个元素,即`(car (cdr lst))`。这个函数通常用于获取一个列表中的第二个元素,因为`cdr`函数可以获取到列表的剩余部分(即除去第一个元素后的所有元素),所以再应用一次`car`函数就可以获取到列表的第二个元素。
- `cddr`函数用于获取列表的第三个元素及其之后的所有元素,即`(cdr (cdr lst))`。这个函数通常用于获取一个列表中除了前两个元素之外的所有元素,因为`cdr`函数可以获取到列表的剩余部分(即除去第一个元素后的所有元素),所以再应用一次`cdr`函数就可以获取到列表的第三个元素及其之后的所有元素。

需要注意的是,这些函数只能用于操作非空的列表。如果尝试对空列表应用`cadr`或`cddr`函数,Scheme解释器将会报错。因此,在实际编程中,我们通常需要先判断列表是否为空,然后再使用这些函数。

posted @ 2023-04-26 08:04  哎呦_不想学习哟~  阅读(127)  评论(0)    收藏  举报