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

IDD*_*DQD 1 lisp common-lisp

这是我的问题:对于我的一个任务,我的任务是开发一个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.任何人都可以告诉我为什么?

Rai*_*wig 9

至少有三种方法:

  1. 使用原始Lisp在函数式编程语言中练习递归
  2. 使用更高阶函数:MAP和REDUCE,以及​​无副作用的编程风格
  3. 使用迭代和副作用

我会告诉你方法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)

以上使用两个高阶函数:REDUCEMAP2.高阶意味着它们将函数作为参数.REDUCE是Common Lisp中的库函数.我们用它来总结一个数字列表.随着MAP2我们与每件价格的项目数相乘计算每个购物车元素的价格.

CL-USER > (calc-total '(shirtA 3 shirtB 1) '(shirtA 25 shirtB 55))
130
Run Code Online (Sandbox Code Playgroud)

摘要

以上方法有几个优点:

  1. 我们有小/紧凑的功能,易于理解和测试
  2. 没有明显的副作用
  3. 映射和缩减的基本模型是在列表处理中经常出现并且易于理解的模式
  4. 功能很容易组合
  5. 'MAP2'功能是一种新的有用的可重用工具
  6. 代码布局要好得多

对于其他方法,这里有提示:

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


Kaz*_*Kaz 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)))
Run Code Online (Sandbox Code Playgroud)

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