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