【问题标题】:Trouble returning recursively-built lists to calling functions in Lisp无法将递归构建的列表返回到 Lisp 中的调用函数
【发布时间】:2014-02-01 22:57:41
【问题描述】:

我是 Lisp 新手,遇到了关于递归和函数返回的问题。为了更好地理解和解决我的问题,我提供了以下场景。如果它很冗长,我很抱歉。如果这会激怒其他人,我很乐意将其减少。要直接跳到业务,请从水平线开始阅读。

想象一下酒吧的女服务员。她没有接受酒水订单,而是强迫她的顾客表明自己是啤酒、朗姆酒、威士忌或这些的某种组合的饮用者。然后她拿起一个装满啤酒、朗姆酒或威士忌的托盘,绕着吧台转了一圈,只给任何自称是该特定饮料饮用者的顾客留下一杯饮料。当她完成每一轮比赛时,她总是坐下来喝一杯长岛冰茶。之后,她又拿起另一盘专门放一种饮料的托盘,再次出去送货。没有人可以拒绝喝酒,也没有人可以改变他们的饮料偏好。

现在,Mindy(女服务员)需要一种新颖的方式来跟踪她为每位顾客提供的每种类型的饮料的数量。明迪数学不是很好,到了晚上,所有的长岛冰茶真的加起来了。

所以当她要求一个简单的解决方案来跟踪她的饮料分配时,我自然建议创建一个简单的 Lisp 小程序。工作原理如下:当她送完一轮美味饮料后,Mindy 只需走到她的 Altair 8800 前并输入以下内容:

(update-orders <order-list> <drink>)

所有顾客的名单和他们的订单在哪里,是她最近一次外出时刚刚供应的饮料。

它应该正确地检查顾客名单和他们的饮料数量,将适当的饮料更新为一种,而让其他人不理会。为了与现有的计算机系统交互,完成后需要从函数中返回一个新更新的订单列表。

就在今天遇到了一个bug:正确调用函数后,返回的值不是我想要的。该列表每次仅包括列表中第一位顾客的最后一个饮料计数列表。与 Lisp 不同,递归编程是真正的罪魁祸首,我尝试修改代码来解决这个问题,但无济于事。我需要从函数返回的完整列表。


你可能已经猜到了,这个故事不是真的。我试图解决的真正问题与微积分有关,对于那些接触 Lisp 的人来说,这是一个众所周知的话题。然而,我的问题不在于我的分配,而是围绕递归调用并将完整的值列表返回给调用函数,以便我可以构建一个完整的所有术语列表,以便在完成时返回。在我能够解决这个子问题之后,我距离将它应用到我的实际任务并解决它只是一箭之遥。

运行以下函数调用:

(update-orders (quote ( (bill (4 beer) (5 whiskey)) (jim (1 beer)) (kenny (1 whiskey) (4 rum)) (abdul (1 beer) (3 whiskey) (2 rum) ))) (quote beer))

让我返回以下内容:

((5 WHISKEY))

相反,我想要一个与提供给上述函数的格式相同的列表。

请看下面的代码。为了方便起见,它已被修改为将调试输出包括到屏幕上。功能饮料清单可能是我的麻烦所在。

(defun update-orders (x d)
    (print 'orders)
    (prin1 x)
    (order (car x) d)
)

(defun order (x d)
    (print 'order)
    (prin1 x)
    (drink-list (cdr x) d)
)

(defun drink-list (x d)
    (print 'drink-list)
    (prin1 x)
    ;(append
    ;(cons
    ;(list
        (drink-count (car x) d)
        (cond
            ((cdr x) (drink-list (cdr x) d))
            (t x)
        )
    ;)
)

(defun drink-count (x d)
    (print 'drink-count)
    (prin1 x)
    (list
        (cond
            ((eq (car (cdr x)) d)    
                (modify-count (car x) 1))
            (t x)
        )
    )
)

(defun modify-count (x d)
    (print 'modify-count)
    (prin1 x)
    (print 'new-modify-count)
    (prin1 (+ (parse-integer (subseq (write-to-string x) 0)) 1))
    (list
        (+ (parse-integer (subseq (write-to-string x) 0)) 1)
    )
)

编辑:

我已将 ooga 的建议合并到我的代码中。新的 order 和 update-order 函数如下所示:

(defun update-orders (x d)
    (cond
        ((null x) ())
        (t        (cons (order (car x) d) (update-orders (cdr x) d)))
    )
)

(defun order (x d)
    ;(print 'order)
    ;(prin1 x)
    (drink-list (cdr x) d)
)

我现在返回以下列表,运行与上面相同的函数调用:

(((5 WHISKEY)) ((1 BEER)) ((4 RUM)) ((2 RUM)))

这是一个嵌入列表的列表(我相信是 2 个深度),其中包括列表中每个顾客的所有最后饮品项目和饮品数量(Bill 的最终列表条目是 5 威士忌,Jim 最终条目是 1 啤酒, ETC)。他们的前 n-1 杯饮料不会添加到他们的饮料返回列表中。

我是否误读了您的建议?我有一种感觉,我离这里只有半步之遥。

【问题讨论】:

  • (update-orders (order-list drink)) 其中order-list 是所有客户及其订单的列表,而“drink”是她最近一次外出时刚刚提供的饮料。“如果order-list 是一个列表,你的意思应该是(update-orders order-list drink),对吧?
  • 我当然做到了,先生。我会修改。我注意到在 Lisp 中这样做的极端倾向。也许是我的 Java 背景?也感谢您的其他收获。

标签: recursion lisp clisp


【解决方案1】:

在更新订单中,您只需将 x 的汽车传递给订单。 x 的其余部分被完全忽略。然后,您只需将 cdr 传递给酒单。

作为代码结构的示例,下面是一个程序,它将给定列表的每个成员加 1。

示例调用:(increment-list '(1 2 3))

(defun increment-list (x)
    (cond
        ((null x) ())
        (t        (cons (increment (car x)) (increment-list (cdr x))))
    )
)

(defun increment (x)
    (+ x 1)
)

increment-list 更改为update-orders 并将increment 更改为orders(并添加第二个输入等),我认为这应该是您的程序结构。

此外,您应该尝试自下而上构建它。尝试编写一个函数,如果(数字饮料)列表中的给定饮料匹配,该函数将在数字上加一。即,鉴于此:

(add-one-if '(4 beer) 'beer)

它应该返回这个

(5 BEER)

鉴于此

(add-one-if '(3 whiskey) 'beer)

它应该返回这个

(3 WHISKEY)

【讨论】:

  • 我已编辑我的帖子以包含您建议的更改。进步,但并不完全。也许我误解了什么。谢谢!
  • 我花了几分钟离开屏幕才完全理解您的答案。在完全掌握了 cons 参数的工作原理之后,我将您的建议合并到了另外几个函数中。这 100% 解决了我的问题,我终于觉得我完全理解了 Lisp 的这个方面(以及一般的编程)。非常感谢,先生。
  • 太好了!乐意效劳。如果您愿意,请将您的解决方案发布为“答案”(您可以发布自己问题的答案),我会看一看。
【解决方案2】:

如上所述,这是我为解决我的问题而实现的完整代码,并结合了 ooga 提供的建议结构。

;;;; WAITING-TABLES

(defun update-orders (x d)
    (cond
        ((null x) ())
        (t        (cons (order (car x) d) (update-orders (cdr x) d)))
    )
)

(defun order (x d)
    (cons (car x)  (drink-list (cdr x) d))
)

(defun drink-list (x d)
    (cond
        ((null x) ())
        (t        (cons (drink-count (car x) d) (drink-list (cdr x) d)))       
    )
)

(defun drink-count (x d)
    (cond
        ((eq (car (cdr x)) d)
            (cons (modify-count (car x) 1) (drink-count (cdr x) d)))
        (t x)
    )
)

(defun modify-count (x d)
    (+ (parse-integer (subseq (write-to-string x) 0)) 1)
)

【讨论】:

  • 这看起来很棒。我唯一能看到的(我可能是错的)是饮料计数和修改计数可以替换为(defun drink-count (x d) (cond ((eq (car (cdr x)) d) (cons (+ (car x) 1) (cdr x))) (t x) ))
  • 谢谢。我还将在我的作业中使用该修改,这是一个基于这个有点相关示例的更复杂的程序。也应该在那里为我节省一些房地产。干杯!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-22
相关资源
最近更新 更多