【问题标题】:How to store increment values in Common Lisp?如何在 Common Lisp 中存储增量值?
【发布时间】:2012-03-31 20:07:56
【问题描述】:

这是我的问题:对于我的一项任务,我的任务是开发一个 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。谁能告诉我为什么?

【问题讨论】:

  • 我建议您使用典型的 Lisp 约定缩进和格式化代码。括号自己的行没有多大帮助。正确缩进(编辑器支持)和括号匹配有帮助。

标签: lisp common-lisp


【解决方案1】:

至少有三种方法:

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

我会告诉你方法2:

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

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

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

Above 使用价格表作为属性列表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. 代码布局更易于阅读

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

  1. 你需要使用递归调用
  2. 您需要为总和使用一个变量,将价格作为副作用添加并在最后返回总和。

【讨论】:

    【解决方案2】:

    您的数据结构是 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 代码格式正确时,你训练你的大脑不要看到括号。不要浪费你的时间来排列你将训练自己无论如何都不会看到的东西。

    【讨论】:

      【解决方案3】:

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

      ;; (print (+ Ttl (* (cadr tLst1) (cadr tLst2)))) 
         (setf Ttl (+ Ttl (* (cadr tLst1) (cadr tLst2))))
         (print Ttl)
      

      【讨论】:

      • ...用荷马辛普森的话来说,“哦!”谢谢。
      • @S.T.A.L.K.E.R.别客气。研究此处的其他答案,它们为您提供了有关如何超越最初最简单的迭代和副作用(即更改变量的值)的编码风格的宝贵信息。
      猜你喜欢
      • 2011-04-13
      • 1970-01-01
      • 2021-12-18
      • 1970-01-01
      • 1970-01-01
      • 2013-08-08
      • 2015-04-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多