这是我的问题:对于我的一个任务,我的任务是开发一个lisp程序,它将2个列表作为输入,一个表示带有项目名称和数量的购物车(L1),第二个表示价格表(L2) ,项目名称和价格.一切都遵循这种格式:
(calcTotal '(shirtA 3 shirtB 1) '(shirtA 25 shirtB 55))
The total is 155.00
Run Code Online (Sandbox Code Playgroud)
这是我的代码如下:
(defun calcTotal (L1 L2 &aux(Ttl 0))
(cond
(
(and (listp L1) (listp L2))
(do
((tLst1 L1 (cddr tLst1)))
((equal tLst1 nil) Ttl)
(do
((tLst2 L2 (cddr tLst2)))
((equal tLst2 nil) nil)
(cond
(
(equal (car tLst1) (car tLst2))
(print (+ Ttl (* (cadr tLst1) (cadr tLst2))))
)
)
)
)
)
)
)
Run Code Online (Sandbox Code Playgroud)
基本上,它所做的是检查第一个列表中项目的名称,然后在第二个列表中搜索它.找到匹配后,将它们的值相乘以得到该项的总和,然后删除第一个列表中的前两个元素,然后重复.问题是总(Ttl)不会累积.我可以专门得到每个项目的金额,但由于某种原因,Ttl返回为0.任何人都可以告诉我为什么?
至少有三种方法:
我会告诉你方法2:
一个重要的工具是功能抽象.使用函数实现自包含功能,可以重复使用,并且可以轻松测试.
你需要一个价格.写一个GET-PRICE
函数.
(defun get-price (item price-list)
(getf price-list item))
Run Code Online (Sandbox Code Playgroud)
以上使用价格表作为财产清单.GETF
查找.您可以将其重新实现为任务.
在列表中应用函数并收集返回值在列表中称为*mapping'.不幸的是,Lisp提供的映射函数只需要一个项目,而不是两个.我们写一个:
(defun map2 (function list)
(loop for (a b) on list by #'cddr
collect (funcall function a b)))
Run Code Online (Sandbox Code Playgroud)
MAP2
映射到列表并在第一个和第二个参数上应用函数,然后在第三个和第四个参数上应用...它将结果收集到新列表中,然后返回该列表.
以上使用了LOOP
构造.您可以使用DO
任务重新实现它.
(defun calc-total (cart price-list)
(reduce #'+
(map2 (lambda (item n)
(* n (get-price item price-list)))
cart)))
Run Code Online (Sandbox Code Playgroud)
以上使用两个高阶函数:REDUCE
和MAP2
.高阶意味着它们将函数作为参数.REDUCE
是Common Lisp中的库函数.我们用它来总结一个数字列表.随着MAP2
我们与每件价格的项目数相乘计算每个购物车元素的价格.
CL-USER > (calc-total '(shirtA 3 shirtB 1) '(shirtA 25 shirtB 55))
130
Run Code Online (Sandbox Code Playgroud)
摘要
以上方法有几个优点:
对于其他方法,这里有提示:
您的数据结构是Lisp属性列表:交替键和值对的平面列表(称为属性"指示符"和属性值).
因此,您应该利用Lisp的属性列表操作函数,例如getf
查找键并检索值.
此外,loop
宏在这里有很大的帮助.它擅长这样的总结工作.
(defun calculate-total (cart prices)
(loop for (item units) on cart by #'cddr
for unit-price = (getf prices item)
if unit-price
sum (* unit-price units)
else
do (error "price check on ~s please!" item)))
Run Code Online (Sandbox Code Playgroud)
请注意我们如何很好地关闭这样的括号)))
.当Lisp代码格式正确时,你训练你的大脑不要看到括号.不要浪费你的时间排队,你会训练自己不要再看.