如何在Common Lisp中存储增量值?

时间:2012-03-31 20:07:56

标签: lisp common-lisp

这是我的问题:对于我的一项任务,我的任务是开发一个lisp程序,它将2个列表作为输入,一个表示带有项目名称和数量的购物车(L1),第二个表示价格表(L2),带有商品名称和价格。一切都遵循这种格式:

(calcTotal '(shirtA 3 shirtB 1) '(shirtA 25 shirtB 55))

The total is 155.00

以下是我的代码:

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

            )

        )

    )
)
)

基本上,它所做的是检查第一个列表中项目的名称,然后在第二个列表中搜索它。找到匹配后,将它们的值相乘以得到该项的总和,然后删除第一个列表中的前两个元素,然后重复。问题是总(Ttl)不会累积。我可以专门得到每个项目的金额,但由于某种原因,Ttl返回为0.有谁可以告诉我为什么?

3 个答案:

答案 0 :(得分:9)

至少有三种方法:

  1. 使用原始Lisp来练习函数式编程语言中的递归
  2. 使用高阶函数:MAP和REDUCE,以及​​无副作用的编程风格
  3. 使用迭代和副作用
  4. 我会告诉你方法2:

    一个重要的工具是功能抽象。使用函数实现自包含功能,可以重复使用,并且可以轻松测试。

    你需要一个价格。写一个GET-PRICE函数。

    (defun get-price (item price-list)
      (getf price-list item))
    

    上面使用价格表作为属性列表GETF进行查找。您可以将其重新实现为任务。

    在列表中应用函数并收集返回值在列表中称为* mapping'。不幸的是,Lisp提供的映射函数只需要一个项目,而不是两个。我们写一个:

    (defun map2 (function list)
      (loop for (a b) on list by #'cddr
            collect (funcall function a b)))
    

    MAP2映射到列表并在第一个和第二个参数上应用函数,然后是第三个和第四个参数,...它将结果收集到新列表中,然后返回该列表。

    上面使用LOOP构造。您可以使用DO作为任务重新实现它。

    (defun calc-total (cart price-list)
      (reduce #'+
              (map2 (lambda (item n)
                      (* n (get-price item price-list)))
                    cart)))
    

    以上使用了两个高阶函数:REDUCEMAP2。高阶意味着它们将函数作为参数。 REDUCE是Common Lisp中的库函数。我们用它来总结一个数字列表。使用MAP2,我们会计算每个购物车元素的价格,方法是将商品数量乘以每件商品的价格。

    CL-USER > (calc-total '(shirtA 3 shirtB 1) '(shirtA 25 shirtB 55))
    130
    

    <强>概要

    以上方法有几个优点:

    1. 我们有小/紧凑的功能,易于理解和测试
    2. 没有明显的副作用
    3. 映射和缩小的基本模型是一种在列表处理中经常出现且易于理解的模式
    4. 功能易于组合
    5. 'MAP2'功能是一种新的有用的可重用工具
    6. 代码布局要好好阅读
    7. 对于其他方法,这里有提示:

      1. 您需要使用递归调用
      2. 您需要使用变量作为总和,将价格添加为副作用并在结尾处返回总和。

答案 1 :(得分:8)

您的数据结构是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)))

请注意我们如何很好地关闭像)))这样的括号。当Lisp代码格式正确时,你训练你的大脑不要看到括号。不要浪费你的时间排队,不管怎样你都要训练自己。

答案 2 :(得分:4)

您只需打印出该值即可。为了以后保留它,还要设置变量:

;; (print (+ Ttl (* (cadr tLst1) (cadr tLst2)))) 
   (setf Ttl (+ Ttl (* (cadr tLst1) (cadr tLst2))))
   (print Ttl)
相关问题