将 lisp 函数转换为使用 map
Convert lisp function to use map
您好,我期待着转换我现有的功能:
(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))))
试试这个,
递归版本:
(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)
=> 无
(checkmember '(1 (2 5) 3) 2)
=> T
(checkmember '(1 2 3) 2)
=> T
(checkmember '((((((((1)))))))) 1)
= T
这个怎么样:
(defun checkMember (L a)
(car (mapcan #'(lambda (e)
(and (equal a e) (list T)))
L)))
注意:它没有递归到列表元素中,但原函数也没有。
映射函数在这里不合适,因为任务涉及搜索列表以确定它是否包含匹配项。这不是映射。
映射意味着通过某个函数传递每个元素(通常以某种方式收集 return 值)。当然,我们可以滥用映射来解决问题。
但是我可以建议这是一个 reduce 问题而不是 mapping 问题吗?减少意味着处理列表的所有元素以生成一个总结该列表的值。
热身:使用reduce
将元素相加:
(reduce #'+ '(1 2 3)) -> 6
在我们的例子中,我们希望以不同的方式缩减列表:根据列表是否包含某些项目,缩减为 T
或 NIL
的单个值。
解决方案:
(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 "threaded" 的累加值。当我们用 +
做一个简单的 reduce 来添加数字时,我们不会考虑这个,但是用于 reduction 的函数的左参数总是部分和。部分和被初始化为零,因为 reduce
首先调用不带参数的 +
函数,这是可能的:(+)
在 Lisp 中为零。
具体来说,在(reduce #'+ '(1 2 3))
中发生的事情是这样的:
- 首先,
reduce
调用 (+)
,其中 return 是 0
。
- 然后,reduce 调用
(+ 0 1)
,生成部分和 1
。
- 接下来,reduce调用
(+ 1 2)
,使用前一个部分和作为左参数,下一个元素作为右参数。这 returns 3
当然。
- 最后,减少调用
(+ 3 3)
,导致 6
。
在我们的例子中,我们通过归约得到的累加值 "threading" 不是部分和,而是布尔值。这个布尔值成为左参数,在缩减函数中称为 found
。我们使用 :initial-value nil
显式指定初始值,因为我们的 lambda 函数不支持不带参数调用。在每次调用我们的 lambda 时,我们都会短路:如果 found
为真,则意味着之前的缩减已经确定列表包含该项目,而我们只是 return 为真。否则,我们检查正确的参数:列表中的下一项。如果等于item
,那么我们returnT
,否则NIL
。而这个 T
或 NIL
则成为下一次调用中的 found
值。一旦我们 return T
,这个值将 "domino" 通过其余的减少,导致 T
return 出 reduce
.
如果你坚持使用映射,你可以这样做:将每个元素映射到一个列表,如果元素不匹配则为空,否则为非空。以列表链接在一起的方式进行映射。如果结果列表是非空的,那么原始列表必须包含一个或多个匹配项:
(defun is-member (list item)
(if (mapcan (lambda (elem)
(if (eq elem item) (list elem))) list)
t))
如果列表包含多次出现的项目,此方法会执行大量浪费分配。
(reduce
方法也很浪费,因为它在很明显 return 值将是 T
之后继续处理列表。)
(defun memb (item list)
(map nil
(lambda (element)
(when (eql item element)
(return-from memb t)))
list))
您好,我期待着转换我现有的功能:
(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))))
试试这个,
递归版本:
(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)
=> 无
(checkmember '(1 (2 5) 3) 2)
=> T
(checkmember '(1 2 3) 2)
=> T
(checkmember '((((((((1)))))))) 1)
= T
这个怎么样:
(defun checkMember (L a)
(car (mapcan #'(lambda (e)
(and (equal a e) (list T)))
L)))
注意:它没有递归到列表元素中,但原函数也没有。
映射函数在这里不合适,因为任务涉及搜索列表以确定它是否包含匹配项。这不是映射。
映射意味着通过某个函数传递每个元素(通常以某种方式收集 return 值)。当然,我们可以滥用映射来解决问题。
但是我可以建议这是一个 reduce 问题而不是 mapping 问题吗?减少意味着处理列表的所有元素以生成一个总结该列表的值。
热身:使用reduce
将元素相加:
(reduce #'+ '(1 2 3)) -> 6
在我们的例子中,我们希望以不同的方式缩减列表:根据列表是否包含某些项目,缩减为 T
或 NIL
的单个值。
解决方案:
(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 "threaded" 的累加值。当我们用 +
做一个简单的 reduce 来添加数字时,我们不会考虑这个,但是用于 reduction 的函数的左参数总是部分和。部分和被初始化为零,因为 reduce
首先调用不带参数的 +
函数,这是可能的:(+)
在 Lisp 中为零。
具体来说,在(reduce #'+ '(1 2 3))
中发生的事情是这样的:
- 首先,
reduce
调用(+)
,其中 return 是0
。 - 然后,reduce 调用
(+ 0 1)
,生成部分和1
。 - 接下来,reduce调用
(+ 1 2)
,使用前一个部分和作为左参数,下一个元素作为右参数。这 returns3
当然。 - 最后,减少调用
(+ 3 3)
,导致6
。
在我们的例子中,我们通过归约得到的累加值 "threading" 不是部分和,而是布尔值。这个布尔值成为左参数,在缩减函数中称为 found
。我们使用 :initial-value nil
显式指定初始值,因为我们的 lambda 函数不支持不带参数调用。在每次调用我们的 lambda 时,我们都会短路:如果 found
为真,则意味着之前的缩减已经确定列表包含该项目,而我们只是 return 为真。否则,我们检查正确的参数:列表中的下一项。如果等于item
,那么我们returnT
,否则NIL
。而这个 T
或 NIL
则成为下一次调用中的 found
值。一旦我们 return T
,这个值将 "domino" 通过其余的减少,导致 T
return 出 reduce
.
如果你坚持使用映射,你可以这样做:将每个元素映射到一个列表,如果元素不匹配则为空,否则为非空。以列表链接在一起的方式进行映射。如果结果列表是非空的,那么原始列表必须包含一个或多个匹配项:
(defun is-member (list item)
(if (mapcan (lambda (elem)
(if (eq elem item) (list elem))) list)
t))
如果列表包含多次出现的项目,此方法会执行大量浪费分配。
(reduce
方法也很浪费,因为它在很明显 return 值将是 T
之后继续处理列表。)
(defun memb (item list)
(map nil
(lambda (element)
(when (eql item element)
(return-from memb t)))
list))