Lisp 简化球拍函数

Lisp 简化球拍函数,lisp,racket,Lisp,Racket,我有以下“更改”功能,它接受一定金额的付款、用于支付的票据/硬币的大小,并返回一个列表,其中包含完成交易后将收到的“硬币”(50美元、20美元、10美元、5美元、2美元和1美元)的数量: (define (change total payment) (let [(x (- payment total))] (change-aux x '(50 20 10 5 2 1)))) (define (change-aux change coins) (cond [(empty?

我有以下“更改”功能,它接受一定金额的付款、用于支付的票据/硬币的大小,并返回一个列表,其中包含完成交易后将收到的“硬币”(50美元、20美元、10美元、5美元、2美元和1美元)的数量:

(define (change total payment)
  (let [(x (- payment total))]
    (change-aux x '(50 20 10 5 2 1))))

(define (change-aux change coins)
  (cond
    [(empty? coins) empty]
    [else (let [(num-coins (floor (/ change (car coins))))]
            (append (list num-coins)
                    (change-aux (- change (* (car coins) num-coins)) (cdr coins))))]))
因此,如果我输入这些参数:

> (change 44 200)
它返回输出:

'(3 0 0 1 0 1)
这是200-44=156,相当于3枚价值50美元的硬币,1枚价值5美元,1枚价值1美元

我的问题是,是否有一种更优雅、更简单的方法来编写类似的过程,而不依赖辅助函数,而是使用lambda、filter、map、foldr、foldl等

提前谢谢。

当然可以

最终解决方案 一步一步地 首先,使用内部
define
,您可以从全局名称空间中去掉辅助函数

(define (change total payment)
  (define (change-aux change coins)
    (cond
      [(empty? coins) empty]
      [else (let [(num-coins (floor (/ change (car coins))))]
              (append (list num-coins)
                      (change-aux (- change (* (car coins) num-coins)) (cdr coins))))]))
  (let [(x (- payment total))]
    (change-aux x '(50 20 10 5 2 1))))

然后,可以将helper函数的一些变量拉入全局函数的lambda列表

(define (change total payment (coins '(50 20 10 5 2 1)))
  (define (change-aux change) ;; eliminate coins in the inner lambda list
    (cond
      [(empty? coins) empty] ;; coins in function body looked up from outer arguments
      [else (let [(num-coins (floor (/ change (car coins))))]
              (append (list num-coins)
                      (change-aux (- change (* (car coins) num-coins)) (cdr coins))))]))
  (let [(x (- payment total))]
    (change-aux x))) ;; eliminate coins in the call

然后,看看
change aux
的代码,我们就知道这实际上是 循环通过并试图拟合当前值的最大倍数 将剩下的差异转化为其他差异——并收集这些差异。可以使用
map
循环并使用
set以对其余部分进行变异

(define (change total payment (coins '(50 20 10 5 2 1)))
  (let ((rest-diff (- payment total)))
    (map (lambda (coin)
           (let ((num-coins (floor (/ rest-diff coin))))
             (set! rest-diff (- rest-diff (* num-coins coin)))
             num-coins))
         coins)))
然后,你像上面那样打电话:

(change 44 200)
;; '(3 0 0 1 0 1)

这里有一个不同的解决方案,它展示了如何在不改变累加器变量的情况下使用左折(reduce),作为现有解决方案的一种功能对比

(defun change (amount coins)
  (reduce-left (tb ((counts . rem) next-coin)
                 (let* ((coin-count (floor rem next-coin))
                        (coin-value (* coin-count next-coin)))
                   (cons (cons coin-count counts)
                         (- rem coin-value))))
               coins
               (cons '() amount)))


3> (change 156 '(50 20 10 5 2 1))
((1 0 1 0 0 3) . 0)

4> (change 88 '(50 20 10 5 2 1))
((1 1 1 1 1 1) . 0)
请注意,这些值最终以相反的顺序报告,并包装在一个额外的
cons
单元格中;可以围绕“管道”使用“陶瓷”函数以预期的形式报告结果

这个想法是我们有一个累加器,它看起来像这样:
(counts.rements)
。存储在
汽车中的
累计器的
计数部分是迄今为止累计的硬币列表。当reduce完成时,将保留最终列表。
cdr
字段保存要处理的剩余金额;因为最后一个硬币是1,所以它将始终显示为零

使用这种累加器结构,我们处理硬币列表

在每次调用reduce内核函数时,左参数是累加器,右参数
next coin
,是下一个硬币面额值

我使用了一个名为
tb
(“树绑定”)的宏,它是一种
lambda
的宏,提供了内置的解构功能,使它看起来像有三个参数

reduce作业的初始值是启动累加器,它有一个空的硬币列表和完整的原始金额:
(cons nil amount)
(为了更好地兼容方案,我将其重写为
(cons'()amount)

reduce函数非常简单:贪婪地计算表示余数所需的下一个硬币值的数量,然后计算新的余数,将它们打包到一个新的累加器
cons
单元格中,该单元格返回,并传递给函数的下一次调用,或在处理硬币值列表时返回


希望这能为“一种更优雅、更简化的方法来编写类似的过程,而不依赖于辅助函数,而是使用lambda、filter、map、foldr、foldl等”,您可以在Racket中解决这个问题。祝你好运

欢迎来到StackOverflow!这个网站是为特定的,客观的编程问题。一般性评论或评论的请求被认为是离题的,应该改为直接提交。谢谢Brian,从现在起将记住它。有没有办法手动将此问题转移到CodeReview?(这里完全是新手)请参阅。函数的界面与描述不符。功能是获取“要支付的金额”(单个输入)。代码引入了两个参数,函数会立即相互减去,并且不再使用。请注意,这个问题与基转换非常相似。例如,以十进制表示1234与使用“硬币”(1000 100 10 1)进行更改是相同的问题。感谢您如此清楚地解释您的解决方案!这将帮助我更好地理解lambdas/映射。这也是一个非常聪明的解决方案。谢谢分享!这将帮助我理解如何在接下来的任务中简化代码。
(defun change (amount coins)
  (reduce-left (tb ((counts . rem) next-coin)
                 (let* ((coin-count (floor rem next-coin))
                        (coin-value (* coin-count next-coin)))
                   (cons (cons coin-count counts)
                         (- rem coin-value))))
               coins
               (cons '() amount)))


3> (change 156 '(50 20 10 5 2 1))
((1 0 1 0 0 3) . 0)

4> (change 88 '(50 20 10 5 2 1))
((1 1 1 1 1 1) . 0)