LISP:为什么mapcan 不接受我的列表作为参数?

2024-01-12

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

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

这并不

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

?

我必须编写一个函数,使用映射函数在给定列表 L 的所有级别上替换列表 D 的所有元素中的一个元素。 我尝试使用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 
         (mapcan #'(lambda (l) (subAll l k d)) l)
         '()))))

但我得到这两个输入的以下结果:

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) with ((and (atom l) (eq l k)) (list 'a 'b))它适用于输入 1。此外,我还制作了自己的函数,仅解构列表并重建它:

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

如果更换它就可以工作((and (atom l) (eq l k)) d) with ((and (atom l) (eq l k)) (lst d))对于我上面的两个输入。

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

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

mapcan 是否只接受特殊类型的列表?如果有人可以向我解释为什么会这样做或提供其他解决方案,我将不胜感激。 (我不能使用任何内置函数,如列表或附加,只有当我制作自己的附加和列表时)

我正在使用 GNU CLISP 2.49


简短回答:

mapcan具有破坏性。

根据超规格条目开启quote http://www.lispworks.com/documentation/HyperSpec/Body/s_quote.htm,

“后果是不明确的 https://en.wikipedia.org/wiki/Undefined_behavior如果文字对象(包括引用的对象)被破坏性修改。”

解决此问题的最简单方法是不使用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> 

长答案:

让我先解决一些风格问题。


Please 正确格式化您的代码 http://dept-info.labri.fr/~idurand/enseignement/lst-info/PFS/Common/Strandh-Tutorial/indentation.html。它会让您和试图帮助您的人更容易阅读。

(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. Also, (cons foo '()), (cons foo nil) and (list foo)都是等价的。您也可以使用最短的。 (你也不need尖锐引用lambda形式,尽管它并没有特别伤害)。

(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 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 is (99 98 3),而不是(99 98)您最初传入的)。在那之后不久的某个时刻,d变成(99 98 3 (2)),此时循环将无限进行,因为您可以在替换对象内找到目标。当我需要时我通常会做什么mapcan正在定义我自己的功能版本。

(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,

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> 

查看这个答案 https://stackoverflow.com/a/18790523/190887(上面已由 Joshua Taylor 链接)了解更多详细信息。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

LISP:为什么mapcan 不接受我的列表作为参数? 的相关文章

  • 在 Django 中为多个查询编写视图的最佳方法?

    这是一个简单的问题 我已经组织了我的模型 以便提供给页面的大多数对象都属于一种类型 项目 该模型包含各种属性 可以帮助我以不同的方式提供服务 我有文章和视频 它们由模型上的 类型 字段确定 类型 文章 等 我有一个列表视图 它显示项目模型中
  • 从通用列表中删除项目

    我有以下方法 我希望从我的收藏中删除与产品 ID 匹配的项目 看起来相当简单 但我有一个例外 基本上我的收藏已经不同步了 那么从集合中删除项目的最佳方法是什么 public void RemoveOrderItem Model Order
  • 检查多个位置的值并仅在源唯一时返回匹配项

    假设我有一个清单Vendors 阿斯达 乐购 Spar 我有一个清单Sources 或者这个类比中的供应商 家乐氏 Kellogg 吉百利 Cadbury 雀巢 Nestle 强生 Johnsons 帮宝适 Pampers Simple 等
  • 同一参数有两个不同的名称有什么意义?

    func mapEachElement inArray arr Int withFunc aFunc Int 为什么会有 inArray 然后 arr 有什么意义 对于 withFunc 和 aFunc 也是如此 它使代码变得更加复杂并且阅
  • 带有泛型类声明的命名空间约束

    我想知道是否 如果可以的话如何 可以将命名空间定义为泛型类声明中的约束参数 我所拥有的是这样的 namespaceMyProject Models Entities namespaceMyProject Tests BaseTest 现在我
  • 是否有一个 jquery List 插件可以自动排序项目并具有强大的添加/删除方法?

    我已经在谷歌上搜索了几个小时 寻找一些东西来处理我的情况 我还不够熟练 无法编写自己的 jquery 插件 该插件应该自动对列表进行排序 这并不像能够轻松地从列表中添加 删除项目那么重要 Themeroller 功能将是一个优点 我基本上会
  • 将不同类型的对象与可比较的对象进行比较

    A java public class A implements Comparable private String id private String name public A String a String b id a name b
  • 以特定方式填充列表

    我需要填充一个包含 5 个位置的列表 new list 我收到 2 个列表 并且有一个默认值来填充新列表 现在开始解决问题 好的方式是 我从列表中接收 2 个值 从列表中接收 2 个值并添加默认值 A1 A2 DEFAULT B1 B2 但
  • 如何在 C# 中使用窗口窗体创建动态下拉列表

    我正在尝试为朋友的手机商店构建一个简单的库存程序 我想使用C 并访问数据库来存储数据 DB 将有 2 个主要列表 devices 品牌 型号 颜色 价格 库存 最小库存 parts 品牌 型号 描述 库存 最小库存 GUI 将使用多个下拉列
  • 如何在 Python 中连接两个列表?

    这个问题的答案是社区努力 help privileges edit community wiki 编辑现有答案以改进这篇文章 目前不接受新的答案或互动 如何在 Python 中连接两个列表 Example listone 1 2 3 lis
  • 在python中组合2个列表

    我有 2 个列表 每个列表大小相同 并且有兴趣将这两个列表组合起来并将其写入文件中 alist 1 2 3 5 blist 2 3 4 5 结果列表应该是这样的 1 2 2 3 3 4 5 5 之后我希望将其写入文件 我怎样才能做到这一点
  • 有没有办法将配置参数传递给 Outlook 插件

    我有一个 JS Outlook 插件 我希望能够将其部署到多个站点 但我希望将相同的代码部署到每个站点并在外部处理配置数据 如果可能 像 process env 适用于 Node js 服务器应用程序 之类的东西适用于客户端应用程序 我发现
  • 从when语句内的函数返回

    我想做的就是使用 when 语句返回一个值 我想要以下功能 if x return y 我正在尝试使用 when x y 但是when语句并没有以退出函数并返回y的方式进行计算 它只是愉快地继续下一行 有没有办法做到这一点而不需要制作一个看
  • 如何从Python列表中的字符串中删除双引号?

    我正在尝试在字典列表中获取一些数据 数据来自 csv 文件 因此都是字符串 文件中的键都有双引号 但由于这些都是字符串 我想删除它们 这样它们在字典中看起来像这样 key value 而不是这个 key value 我尝试简单地使用 str
  • 将 Repeater Container.ItemIndex 传递给 Javascript 函数

    在 C Asp Net 中 我需要将中继器出现索引传递到 Javascript 函数中OnClientClick从 ASP 按钮按下 这是我的代码
  • Django查询:如何过滤对象以排除列表中的id?

    如何在查询中进行过滤 以便结果排除 ID 属于列表的任何对象实例 可以说我有 object id list 1 5 345 MyObject objects filter Q time gte datetime now Q what to
  • 根据传递的参数覆盖 Javascript 函数

    是否可以根据传递给函数的参数数量来重写函数 例如 function abc name document write My name is name function abc name friend document write My nam
  • 使用 for 循环填充 python 字典列表

    我试图用 for 循环填充字典列表 但最终结果显示 for 循环填充的最后一个字典覆盖了所有先前字典的值 我尝试调整以下中提出的解决方案 如何使用循环填充 Python 字典 https stackoverflow com question
  • 属性错误:“列表”对象没有属性“拆分”

    我正在尝试读取一个文件并用逗号分隔每行中的一个单元格 然后仅显示第一个和第二个单元格 其中包含有关纬度和经度的信息 这是文件 time 纬度 经度 类型2015 03 20T10 20 35 890Z 38 8221664 122 7649
  • Collections.sort(list) 和 list.sort(Comparator) 之间的区别

    有什么理由让我应该选择Collections sort list 方法而不是简单地调用list sort 内部Collections sort只是调用sort的方法List无论如何 上课 令人惊讶的是几乎每个人都告诉我使用Collectio

随机推荐