问题描述
嘿所以我试图在 lisp 中创建一个函数,它接受三个参数,一个跑步者列表,一个名字和一个奖牌类型。跑步者名单如下所示:
((bolt ((gold 4)(silver 2)))
(farah ((gold 3)(silver 1)(bronze 1)))
(ottey ((bronze 3))))
我正在尝试更新每个跑步者的奖牌类型和数量,例如如果我想让螺栓获得 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)
解决方法
所以,首先让我们将这些 ((key value) ...)
列表称为 mlist(如果您愿意,可以称为“奖牌列表”):它们实际上是关联列表(alists),但关联列表通常采用 {{ 1}},所以我想要另一个名字。
让我们编写一个通用函数 ((key . value) ...)
来更新 mlist。它会:
- 无事可做就停下来;
- 否则,如果 mlist 的第一个元素是它要查找的元素,则对该元素的值调用其更新函数并返回一个新的 mlist;
- 否则返回一个包含现有第一个元素的新 mlist,并更新 mlist 的其余部分。
这是:
update-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)))))
的有趣之处在于它是一个 mlist,其中每个元素的值都是一个 mlist。因此,我们要做的就是使用 *medals*
,其中更新程序函数本身调用 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
计数增加 1:
gold
但是我们有一个小问题:
> (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))))
以便,如果它到达 mlist 的末尾,它会提供回退:
update-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))
您可能已经注意到,每次我调用这些函数时,就好像这是第一次:那是因为它们是函数,它们有参数和返回值,而这些值它们返回的是新结构:它们不会破坏性地修改它们的参数。这意味着它们更容易推理和理解,因为它们就是所谓的 referentially transparent,并且它们可以轻松安全地组合:
> (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)))
好吧,我们也可以写一个小函数来做到这一点:
> (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))))
(两个版本)有什么问题。如果您的 mlist 中真的有很多人,会发生什么? - 你能不能写一个
update-mlist
的版本,它并不真正关心整个奖牌颁发的事情,而且它可以对任何函数执行这个技巧?那会有用吗?