有没有办法只使用应用程序编程并避免递归或迭代作为编程风格在 Common Lisp 中实现 mapcar?

问题描述

我正在尝试通过Common Lisp:符号计算的温和介绍这本书来学习 Common Lisp。此外,我正在使用 SBCL、Emacs 和 Slime。

在第 7 章中,作者建议本书将涵盖三种编程风格:递归、迭代和应用程序编程。

我对最后一个感兴趣。这种风格以 applicative operator funcall 而闻名,它是负责其他 applicative 运算符(如 mapcar)的原语。

因此,出于教育目的,我决定使用 mapcar 实现我自己的 funcall 版本:

(defun my-mapcar (fn xs)
  (if (null xs)
      nil
      (cons (funcall fn (car xs))
            (my-mapcar fn (cdr xs)))))

如您所见,我使用递归作为一种编程风格来构建标志性的应用程序编程函数

它似乎有效:

CL-USER> (my-mapcar (lambda (n) (+ n 1)) (list 1 2 3 4))
(2 3 4 5)

CL-USER> (my-mapcar (lambda (n) (+ n 1)) (list ))
NIL

;; comparing the results with the official one

CL-USER> (mapcar (lambda (n) (+ n 1)) (list ))
NIL

CL-USER> (mapcar (lambda (n) (+ n 1)) (list 1 2 3 4))
(2 3 4 5)

有没有办法在不使用递归或迭代的情况下实现 mapcar?仅使用应用性编程作为一种风格?

谢谢。

Obs.:我试着看看它是如何实现的。但这是不可能的

CL-USER> (function-lambda-expression #'mapcar)
NIL
T
MAPCAR

我还使用 Emacs M-. 来查找文档。但是,以下几点对我没有帮助。我使用 this 找到以下文件

/usr/share/sbcl-source/src/code/list.lisp
  (DEFUN MAPCAR)
/usr/share/sbcl-source/src/compiler/seqtran.lisp
  (:DEFINE-SOURCE-TRANSFORM MAPCAR)
/usr/share/sbcl-source/src/compiler/fndb.lisp
  (DECLaim MAPCAR SB-C:DEFKNowN)

解决方法

coxme(latency ~ tests + (1|ID),data = df) Error in if (n == 0) stop("No observations remain in the data set") : argument is of length zero 本身是一个原始的应用运算符(Common Lisp 的第 220 页:符号计算的温和介绍)。所以,如果你想以一种应用的方式重写它,你应该使用其他一些原始的应用运算符,例如 mapmap-into。例如,使用 df <- data.frame(ID = rep(c(LETTERS[1:20]),each=4),tests = rep(letters[1:4],replace = FALSE),time = abs(rnorm(80,mean = 10,sd = 4)),latency = ifelse(time > 20,20,time)) :

mapcar
,

实现 mapcar 的另一种方法是使用更通用的 reduce 函数(也称为折叠)。让我们将用户提供的函数命名为 f 并定义 my-mapcar

reduce 函数携带一个累加器值,用于构建结果列表,这里它将采用值 v、子列表 rest,并调用 cons (funcall f v)rest,以构建列表。

更准确地说,这里 reduce 将实现右折叠,因为 cons 是右结合的(例如递归列表是“右侧”,即cons,例如 (cons a (cons b (cons nil))))。

为了用reduce定义一个右折叠,你传递:from-end t,这表示它从最后一个元素和初始累加器建立一个值来获得一个新的累加器值,然后使用该新累加器的倒数第二个元素来构建新的累加器,等等。这是确保结果元素与输入列表的顺序相同的方法。

在这种情况下,reduce 函数将当前元素作为第一个参数,将累加器作为第二个参数。

由于元素的类型和累加器的类型不同,你需要为累加器传递一个:initial-value(从列表中获取初始值的默认行为是像{ {1}} 或 +,其中累加器与列表元素在同一域中)。

考虑到这一点,你可以这样写:

*

例如:

(defun my-map (f list)
  (reduce (lambda (v rest) (cons (funcall f v) rest))
          list
          :from-end t
          :initial-value nil))