Skip to content

LISP - 在列表上使用 `mapcar`

在 Common Lisp 中,mapcar 是函数式编程风格的基石。它提供了一种简洁优雅的方式,将函数应用于一个或多个列表的每个元素,生成一个包含结果的新列表。它是非破坏性的,这意味着原始列表永远不会被修改。

(mapcar function list &rest more-lists)
  • function:要应用的函数。它必须接受与提供给 mapcar 的列表数量相同数量的参数。
  • list & more-lists:一个或多个要处理的列表。

mapcar 的工作方式是:从每个列表中取出第一个元素,将其传递给函数,并收集结果。然后对第二个元素、第三个元素等重复此操作,直到达到最短列表的末尾。

最简单的用例是转换单个列表。

;; 让我们定义一个数字列表
(defvar *my-numbers* '(1 2 3 4 5))
;; 使用命名函数:将每个数字递增
;; #'1+ 是 (function 1+) 的简写
(print (mapcar #'1+ *my-numbers*))
;; 使用 lambda 函数:平方每个数字
(print (mapcar (lambda (n) (* n n)) *my-numbers*))
(2 3 4 5 6)
(1 4 9 16 25)

这就是 mapcar 真正发挥作用的地方。它可以并行处理多个列表。

;; 将两个列表的对应元素相加
(print (mapcar #'+
'(1 2 3) ; #'+ 的第一个参数
'(10 20 30))) ; #'+ 的第二个参数
;; 当最短的列表耗尽时停止
(print (mapcar #'+
'(1 2) ; 最短的列表
'(10 20 30)))
(11 22 33)
(11 22)

示例 3:使用字符串和其他数据类型

Section titled “示例 3:使用字符串和其他数据类型”

mapcar 不仅限于数字。它可以操作任何数据类型。

;; 让我们将函数应用于字符串列表
(defvar *words* '("Welcome" "To" "Modern" "Lisp"))
;; 获取每个单词的长度
(print (mapcar #'length *words*))
;; 将每个单词转换为小写
(print (mapcar #'string-downcase *words*))
(7 2 6 4)
("welcome" "to" "modern" "lisp")

Lisp 提供了几种映射函数。选择正确的函数可以提高代码清晰度。

  • mapcar:当您想根据应用函数的结果创建一个新列表时使用。这是最常见的情况。
  • mapc:当您需要为副作用(例如,打印、修改哈希表)调用函数,并且您不需要结果时使用。它返回其第一个列表参数。
  • map:一个更通用的函数。它可以生成指定类型的序列(例如,向量而不是列表)。(map 'vector #'1+ '(1 2 3)) 返回 #(2 3 4)。
  • maplist:当您的函数需要操作列表的连续子列表(cdrs)而不仅仅是元素时使用。

针对您的任务使用最具体的函数。如果您正在将列表转换为新列表,mapcar 是惯用的选择。如果您只是打印每个元素,mapc 更高效,并且更清晰地表明了您的意图。

;; 使用 mapc 进行副作用操作(打印)
(mapc (lambda (word) (format t "~a " word)) *words*)
;; 输出:Welcome To Modern Lisp