转换lisp函数以使用map

时间:2015-02-16 06:48:29

标签: lisp common-lisp

您好我期待转换我现有的功能:

(defun checkMember (L A)
  (cond
    ((NULL L) nil)
    ( (and (atom (car L)) (equal (car L) A))  T )
    (T  (checkMember (cdr L) A))))

要使用地图功能,但老实说,我真的无法理解地图功能是如何工作的,你能否告诉我这个功能是如何工作的?

这是我的尝试:

(defun checkMem (L A)
  (cond
    ((NULL L) nil)
    ( (and (atom (car L)) (equal (car L)  (car A)))  T )
    (T  (mapcar #'checkMem  (cdr L) A))))

4 个答案:

答案 0 :(得分:7)

这里的映射函数不合适,因为该任务涉及搜索列表以确定它是否包含匹配项。这不是映射。

映射意味着将每个元素传递给某个函数(通常以某种方式收集返回值)。当然,我们可以滥用映射以某种方式解决问题。

但是我可以建议这是 reduce 问题而不是映射问题吗?减少意味着处理列表的所有元素,以便生成一个汇总该列表的单个值。

热身:使用reduce将元素添加到一起:

(reduce #'+ '(1 2 3)) -> 6

在我们的案例中,我们希望以不同方式缩小列表:根据列表是否包含某个项目,将TNIL设置为单个值。

解决方案:

(defun is-member (list item)
  (reduce (lambda (found next-one) (or found (eql next-one item)))
          list :initial-value nil))

;; tests:
(is-member nil nil) -> NIL
(is-member nil 42) -> NIL
(is-member '(1) 1) -> T
(is-member '(1) 2) -> NIL
(is-member '(t t) 1) -> NIL  ;; check for accumulator/item mixup
(is-member '(1 2) 2) -> T
(is-member '(1 2) 3) -> NIL
...

使用(左关联)reduce函数的一个常见模式是将每个缩减中的左参数视为一个累积值,该值是"线程"通过减少。当我们使用+进行简单的简化以添加数字时,我们不会考虑这一点,但用于缩减的函数的左参数始终是部分和。部分和被初始化为零,因为reduce首先调用没有参数的+函数,这是可能的:(+)在Lisp中为零。

具体而言,(reduce #'+ '(1 2 3))中发生的事情是:

  • 首先,reduce调用(+),返回0
  • 然后,减少调用(+ 0 1),这会产生部分和1
  • 接下来,使用前一个部分和作为左参数,减少调用(+ 1 2),将下一个元素作为右参数。当然,这会返回3
  • 最后,减少调用(+ 3 3),从而产生6

在我们的例子中,我们的累计值是"线程"通过减少不是一个部分和,而是一个布尔值。这个布尔值成为左参数,在reduce函数中称为found。我们使用:initial-value nil显式指定初始值,因为我们的lambda函数不支持在没有参数的情况下调用。在每次调用lambda时,我们都会短路:如果found为真,则表示先前的减少已经确定列表包含该项,我们只返回true。否则,我们检查正确的参数:列表中的下一个项目。如果它等于item,则我们返回T,否则返回NIL。然后,此TNIL将成为下一次调用中的found值。一旦我们返回T,此值将会是" domino"通过剩余的缩减,导致T返回reduce

如果您坚持使用映射,则可以执行以下操作:将每个元素映射到列表,如果元素与项目不匹配,则列表为空,否则为非空。以这样的方式进行映射,即将列表连接在一起。如果结果列表为非空,则原始列表必须包含项目的一个或多个匹配项:

(defun is-member (list item)
  (if (mapcan (lambda (elem)
                (if (eq elem item) (list elem))) list)
     t))

如果列表中包含多次出现的项目,则此方法会执行大量浪费的分配。

reduce方法也很浪费,因为在显然返回值为T后,它会继续处理列表。)

答案 1 :(得分:3)

这个怎么样:

(defun checkMember (L a)
  (car (mapcan #'(lambda (e)
                   (and (equal a e) (list T)))
               L)))

注意:它不会递归到列表元素中,但原始函数也没有。

答案 2 :(得分:3)

(defun memb (item list)
  (map nil
       (lambda (element)
         (when (eql item element)
           (return-from memb t)))
       list))

答案 3 :(得分:1)

试试这个,

递归版:

(defun checkmember (l a)
 (let ((temp nil))
  (cond ((null l) nil) ((find a l) (setf temp (or temp t)))
   (t
    (mapcar #'(lambda (x) (cond ((listp x)(setf temp (or temp (checkmember x a))))))
     l)))
  temp))

用法:(checkmember '(1 (2 5) 3) 20) => NIL

(checkmember '(1 (2 5) 3) 2) => Ť

(checkmember '(1 2 3) 2) => Ť

(checkmember '((((((((1)))))))) 1) = T