【问题标题】:lisp updating a list functionlisp 更新列表函数
【发布时间】:2021-04-24 13:52:36
【问题描述】:

嘿,所以我试图在 lisp 中创建一个函数,它接受三个参数,一个跑步者列表,一个名字和一个奖牌类型。跑步者列表如下所示:

((bolt ((gold 4)(silver 2)))
(farah ((gold 3)(silver 1)(bronze 1)))
(ottey ((bronze 3))))

我正在尝试更新每个跑步者的奖牌类型和数量,例如如果我想让 Bolt 获得 4 枚金牌,那么我可以使用此功能相应地更新列表。我对 lisp 很陌生,我正在努力做到这一点,我尝试使用 dolist() 遍历列表,但我正在努力解决它背后的逻辑。我该怎么做呢?

(defun update (type name list) 
    (setf medal (get-runner(name *runner)) )
    (if  ((assoc ‘medal medals) != nil) ;
        (setf count (assoc ‘medal medals)+1)
        (new-list (assoc ‘medal medals) count)



【问题讨论】:

  • 您已将此标记为emacsclisp。如果您的意思是 Emacs 编辑器,那与问题无关。如果您的意思是 Emacs 的编程语言 elisp,那么它和 clisp 是两种不同的语言,您应该明确您使用的是哪种语言。
  • @amalloy 嗨,我正在使用 emacs 编辑器编写此代码,并在 lisp 中编写此代码
  • “更新列表”:您应该指定是否要创建具有更新值的新列表,或者是否要执行“就地更新”旧列表(即副作用)。
  • @Renzo 我想做一个就地更新,但我也愿意创建一个新列表
  • 您的代码中有几个语法错误。请记住,函数和运算符总是使用前缀符号 (/= a b) 而不是 (a != b) 调用,并且必须正确使用括号。你可以使用一些好的介绍材料。网上有很多免费的。

标签: lisp common-lisp clisp


【解决方案1】:

所以,首先让我们将这些列表称为((key value) ...) mlists(如果您愿意,可以称为“奖牌列表”):它们实际上是关联列表(alists),但关联列表通常采用((key . value) ...) 的形式,所以我想要另一个名字。

让我们编写一个通用函数update-mlist 来更新一个mlist。它将:

  • 如果无事可做就停下来;
  • 否则,如果 mlist 的第一个元素是它要查找的元素,则根据该元素的值调用其更新程序函数并返回一个新的 mlist;
  • 否则返回包含现有第一个元素的新 mlist,并更新 mlist 的其余部分。

这里是:

(defun update-mlist (mlist key updater)
  ;; update an mlist, replacing the element with key KEY by calling
  ;; UPDATER on its value.  An mlist is of the form ((key value) ...).
  (cond 
   ((null mlist)
    ;; no more to process: we're done
    '())
   ((eql (first (first mlist)) key)
    ;; found it: call the updater on the value and return the new
    ;; mlist
    (cons (list (first (first mlist))
                (funcall updater (second (first mlist))))
          (rest mlist)))
   (t 
    ;; didn't find it: search the rest
    (cons (first mlist)
          (update-mlist (rest mlist) key updater)))))

我们可以试试这个:

> (update-mlist '((able 1) (baker 2) (charlie 2))
                'charlie
                (lambda (v)
                  (+ v 1)))
((able 1) (baker 2) (charlie 3))

好的。

所以,现在,让我们将奖牌列表存储在一个变量中,以便我们讨论它:

(defvar *medals* '((bolt ((gold 4)
                          (silver 2)))
                   (farah ((gold 3)
                           (silver 1)
                           (bronze 1)))
                   (ottey ((bronze 3)))))

*medals* 的有趣之处在于它是一个 mlist,其中每个元素的值都是一个 mlist。所以我们要做的事情是使用update-mlist 更新函数本身调用update-mlist 来更新奖牌列表。好的,好吧,我们可以这样写:

(defun update-medals (medals person medal updater)
  ;; update the medal mlist for PERSON, calling UPDATER on the value
  ;; of the MEDAL medal
  (update-mlist medals person
                (lambda (medal-mlist)
                  (update-mlist medal-mlist
                                medal
                                updater))))

就是这样。假设farah 刚刚获得金牌:我们想将他们的gold 计数增加 1:

> (update-medals *medals* 'farah 'gold
                 (lambda (count)
                   (+ count 1)))
((bolt ((gold 4) (silver 2)))
 (farah ((gold 4) (silver 1) (bronze 1)))
 (ottey ((bronze 3))))

但是我们有一个小问题:

>  (update-medals *medals* 'ottey 'gold
                  (lambda (count)
                    (+ count 1)))
((bolt ((gold 4) (silver 2)))
 (farah ((gold 3) (silver 1) (bronze 1)))
 (ottey ((bronze 3))))

亲爱的。

所以,好吧,我们可以解决这个问题:让我们更改 update-mlist,这样,如果它到达 mlist 的末尾,它会提供一个回退:

(defun update-mlist (mlist key updater fallback)
  ;; update an mlist, replacing the element with key KEY by calling
  ;; UPDATER on its value.  An mlist is of the form ((key value) ...).
  ;; If we reach the end of the list add an entry for KEY with FALLBACK
  (cond 
   ((null mlist)
    ;; no more to process: add the fallback
    (list (list key fallback)))
   ((eql (first (first mlist)) key)
    ;; found it: call the updater on the value and return the new
    ;; mlist
    (cons (list (first (first mlist))
                (funcall updater (second (first mlist))))
          (rest mlist)))
   (t 
    ;; didn't find it: search the rest
    (cons (first mlist)
          (update-mlist (rest mlist) key updater fallback)))))

我们可以测试一下:

> (update-mlist '((able 1) (baker 2) (charlie 3))
                'zebra
                (lambda (v)
                  (+ v 1))
                26)
((able 1) (baker 2) (charlie 3) (zebra 26))

我们需要相应地更改update-medals

(defun update-medals (medals person medal updater fallback)
  ;; update the medal mlist for PERSON, calling UPDATER on the value
  ;; of the MEDAL medal.  If there is no entry add a fallback.  If
  ;; there is no entry for the person add a fallback as well
  (update-mlist medals person
                (lambda (medal-mlist)
                  (update-mlist medal-mlist
                                medal
                                updater
                                fallback))
                (list medal fallback)))

这很有效:

> (update-medals *medals* 'ottey 'gold
                 (lambda (count)
                   (+ count 1))
                 1)
((bolt ((gold 4) (silver 2)))
 (farah ((gold 3) (silver 1) (bronze 1)))
 (ottey ((bronze 3) (gold 1))))

> (update-medals *medals* 'hercules 'gold
                 (lambda (count)
                   (+ count 100))
                 100)
((bolt ((gold 4) (silver 2)))
 (farah ((gold 3) (silver 1) (bronze 1)))
 (ottey ((bronze 3)))
 (hercules (gold 100)))

好的,最后我们可以将这一切包装在一个 award-medal 函数中:

(defun award-medal (medals person medal &optional (number 1))
  (update-medals medals person medal
                 (lambda (c)
                   (+ c number))
                 number))

现在

> (award-medal *medals* 'bolt 'gold)
((bolt ((gold 5) (silver 2)))
 (farah ((gold 3) (silver 1) (bronze 1)))
 (ottey ((bronze 3))))

> (award-medal *medals* 'ottey 'gold)
((bolt ((gold 4) (silver 2)))
 (farah ((gold 3) (silver 1) (bronze 1)))
 (ottey ((bronze 3) (gold 1))))

> (award-medal *medals* 'hercules 'diamond 10000)
((bolt ((gold 4) (silver 2)))
 (farah ((gold 3) (silver 1) (bronze 1)))
 (ottey ((bronze 3)))
 (hercules (diamond 10000)))

您可能已经注意到,每次我调用这些函数中的一个时,就好像这是第一次:那是因为它们是函数,它们有参数和返回值,而值他们返回的是新结构:他们不会破坏性地修改他们的论点。这意味着它们都更容易推理和理解,因为它们就是所谓的referentially transparent,并且可以轻松安全地组合它们:

> (award-medal (award-medal *medals* 'bolt 'gold)
               'ottey 'silver)
((bolt ((gold 5) (silver 2)))
 (farah ((gold 3) (silver 1) (bronze 1)))
 (ottey ((bronze 3) (silver 1))))

好吧,我们也可以编写一个小函数来实现这一点:

(defun award-medals (medals award-mlist)
  (if (null award-mlist)
      medals
    (award-medals (award-medal medals
                               (first (first award-mlist)) 
                               (second (first award-mlist)))
                  (rest award-mlist))))

现在

> (award-medals *medals*
                '((bolt gold) (ottey silver) (farah bronze)))
((bolt ((gold 5) (silver 2)))
 (farah ((gold 3) (silver 1) (bronze 2)))
 (ottey ((bronze 3) (silver 1))))

最后两件事:

  1. update-mlist(两个版本)有什么“错误”。如果您的 mlist 中有很多人,会发生什么?
  2. 你能写一个award-medals 的版本,它并不真正关心整个奖牌授予的事情,而且它可以为任何功能做这个技巧吗?这会有用吗?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-28
    • 2013-01-10
    相关资源
    最近更新 更多