LISP:为什么mapmap不能接受我的列表作为参数? [英] LISP: Why doesn't mapcan accept my list give as parameters?

查看:89
本文介绍了LISP:为什么mapmap不能接受我的列表作为参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为简化我的问题: 为什么有效

To simplify my question: why this works

(mapcan #'(lambda (l) (list '1 '2) ) '(a b))

这不是

(mapcan #'(lambda (l) '(1 2) ) '(a b))

?

我必须编写一个函数,该函数使用Map函数在给定列表L的所有级别上替换列表D的所有元素. 我尝试为此使用mapcan:

I have to write a function that substitutes an element through all elements of list D at all levels of a given list L, using Map Functions. I tried using mapcan for this:

(defun subAll (l k d)
  (cond 
    ((and (atom l) (eq l k)) 
     d)
    ((and (atom l) (not (eq l k)))
     (cons l '()))
    (t (cons 
         (mapcan #'(lambda (l) (subAll l k d)) l)
         '()))))

但是对于这2个输入,我得到以下结果:

but I get following results for these 2 inputs:

1.

(subAll '(1(2(3(4(5(6(7)))))) (2(3(4 7(4(4(4(4)))))))) '7 '(a b))
 => ((1(2(3(4(5(6(A B(4(4(4(4)))))))))) (2(3(4 A B(4(4(4(4)))))))))

2.

(subAll '(1 2 3 (4 2 (3 2 (2)))) '2 '(99 98))
=>Lisp stack overflow.

但是如果更换 ((and (atom l) (eq l k)) d)((and (atom l) (eq l k)) (list 'a 'b))可以用于输入1.此外,我已经制作了自己的函数,该函数只解构一个列表并对其进行重构:

But if replace ((and (atom l) (eq l k)) d) with ((and (atom l) (eq l k)) (list 'a 'b)) it works for input 1. Also I`ve made my own function that just deconstructs a list and reconstructs it:

(defun lst(l)
(cond
    ((null l) nil)
    (t (cons (car l) ( lst (cdr l))))))

,并且如果我在上述两个输入中都将((and (atom l) (eq l k)) d)替换为((and (atom l) (eq l k)) (lst d)),则可以正常工作.

and it work if a replace ((and (atom l) (eq l k)) d) with ((and (atom l) (eq l k)) (lst d)) for both of my inputs above.

  1. => ((1(2(3(4(5(6(A B)))))) (2(3(4 A B(4(4(4(4)))))))))

=> ((1 A B 3 (4 A B (3 A B (A B)))))

map只能接受一种特殊的列表吗?如果有人可以向我解释为什么这样做或提供其他解决方案,我将不胜感激. (仅当我创建自己的追加和列表时,才能使用列表或追加等内置函数)

Does mapcan accept only a special kind of list? If anyone can explain to me why it does that or give another solution I would be grateful. ( I can't use any built in functions like list or append, only if I make my own append and list)

我正在使用GNU CLISP 2.49

I am using GNU CLISP 2.49

推荐答案

简短答案:

mapcan是破坏性的.

根据 quote 上的Hyperspec条目,

As per the Hyperspec entry on quote,

如果破坏性地修改了文字对象(包括引用的对象),后果是未定义. "

最简单的解决方法是不使用mapcan.

The easiest way to fix this is not to use mapcan.

(defun subAll (l k d)
  (cond 
    ((and (atom l) (eq l k)) 
     d)
    ((and (atom l) (not (eq l k)))
     (cons l '()))
    (t (cons 
         (loop for elem in l append (subAll elem k d))
         '()))))

具有该定义,

CL-USER> (suball '(1 2 3 (4 2 (3 2 (2)))) '2 '(99 98))
((1 99 98 3 (4 99 98 (3 99 98 (99 98)))))
CL-USER> 

详细答案:

让我先解决一些样式问题.

Let me get some style issues out of the way first.

格式您的代码正确.对于您和试图帮助您的人来说,它都使阅读起来更加容易.

Please format your code properly. It'll make it easier to read, both for you and people trying to help you.

(defun subAll (l k d)
  (cond 
    ((and (atom l) (eq l k)) 
     d)
    ((and (atom l) (not (eq l k)))
     (cons l '()))
    (t (cons 
         (mapcan #'(lambda (l) (subAll l k d)) l)
         '()))))


接下来,Common Lisp的标准命名样式是train-case.同样,(cons foo '())(cons foo nil)(list foo)都是等效的.您最好使用最短的一个. (您也不必 用引号引起来的lambda表格,尽管它并不特别受伤害).


Next, the standard naming style for Common Lisp is train-case. Also, (cons foo '()), (cons foo nil) and (list foo) are all equivalent. You may as well use the shortest one. (You also don't need to sharp-quote lambda forms, though it doesn't particularly hurt).

(defun sub-all (l k d)
  (cond 
    ((and (atom l) (eq l k)) 
     d)
    ((atom l)
     (list l))
    (t (list (mapcan #'(lambda (l) (sub-all l k d)) l)))))


让我们看一下在stack overflow情况下函数运行时发生了什么情况.


Lets take a look at what happens to your function as it runs during that stack overflow case.

; SLIME 2013-04-02
CL-USER> (defun sub-all (l k d)
  (cond 
    ((and (atom l) (eq l k)) 
     d)
    ((atom l)
     (list l))
    (t (list (mapcan #'(lambda (l) (sub-all l k d)) l)))))
;Compiler warnings :
;   In an anonymous lambda form inside SUB-ALL: Undefined function SUB-ALL
SUB-ALL
CL-USER> (trace sub-all)
NIL
CL-USER> (sub-all '(1 2 3 (4 2 (3 2 (2)))) '2 '(99 98))
0> Calling (SUB-ALL (1 2 3 (4 2 (3 2 (2)))) 2 (99 98)) 
 1> Calling (SUB-ALL 1 2 (99 98)) 
 <1 SUB-ALL returned (1)
 1> Calling (SUB-ALL 2 2 (99 98)) 
 <1 SUB-ALL returned (99 98)
 1> Calling (SUB-ALL 3 2 (99 98)) 
 <1 SUB-ALL returned (3)
 1> Calling (SUB-ALL (4 2 (3 2 (2))) 2 (99 98 3)) 
  2> Calling (SUB-ALL 4 2 (99 98 3)) 
  <2 SUB-ALL returned (4)
  2> Calling (SUB-ALL 2 2 (99 98 3)) 
  <2 SUB-ALL returned (99 98 3)
  2> Calling (SUB-ALL (3 2 (2)) 2 (99 98 3)) 
   3> Calling (SUB-ALL 3 2 (99 98 3)) 
   <3 SUB-ALL returned (3)
   3> Calling (SUB-ALL 2 2 (99 98 3)) 
   <3 SUB-ALL returned (99 98 3)
   3> Calling (SUB-ALL (2) 2 (99 98 3)) 
    4> Calling (SUB-ALL 2 2 (99 98 3)) 
    <4 SUB-ALL returned (99 98 3)
   <3 SUB-ALL returned ((99 98 3))
  <2 SUB-ALL returned ((3 99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 (99 98 3 ...

您实际上并没有看到关键的突变点,但是可以看到更早的等效点(请注意,在第二个层"调用中,d(99 98 3),而不是您通过的(99 98)最初).此后不久,d变为(99 98 3 (2)),这时循环变为无限,因为您可以在替换对象内找到目标.通常我在需要mapcan时要做的就是定义自己的功能版本.

You never actually get to see the critical mutation point, but you can see an earlier equivalent (notice that by the second "layer" of calls, d is (99 98 3), rather than the (99 98) that you passed in initially). At some point shortly after that, d becomes (99 98 3 (2)), at which point the loop goes infinite because you can find your target inside of your replacement. What I generally end up doing when I need mapcan is defining my own functional version.

(defun mappend (fn list)
  (loop for elem in list
     append (funcall fn elem)))

(defun sub-all (tree target replacement)
  (cond 
    ((and (atom tree) (eq tree target)) 
     replacement)
    ((atom tree)
     (list tree))
    (t (list 
         (mappend 
           (lambda (sub) 
             (sub-all sub target replacement))
           tree)))))

这也可以避免引用列表的未定义行为.具体来说,使用mappend的上述定义,

This also gets around the Undefined behavior for quoted lists. Specifically, with the above definition of mappend,

CL-USER> (mappend #'(lambda (l) '(1 2)) '(a b))
; in: MAPPEND #'(LAMBDA (L) '(1 2))
;     #'(LAMBDA (L) '(1 2))
; 
; caught STYLE-WARNING:
;   The variable L is defined but never used.
; 
; compilation unit finished
;   caught 1 STYLE-WARNING condition
(1 2 1 2)
CL-USER> (mappend #'(lambda (l) (declare (ignore l)) '(1 2)) '(a b))
(1 2 1 2)
CL-USER> 

查看此答案(上面已由约书亚·泰勒(Joshua Taylor)链接)以获取更多详细信息.

Check out this answer (already linked by Joshua Taylor above) for more details.

这篇关于LISP:为什么mapmap不能接受我的列表作为参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆