SRFI 1scheme 的标准的一种补充,包括 Racket 在内的 scheme 实现(变种、方言),通常都会支持大部分的 SRFI。 SRFI 1 是关于 list 的,它提供几十个常用的 list 函数,对 R4RS/R5RS两版 scheme 标准来说非常有用, 因为这两版标准删除了很多常用的 list 函数。

SRFI 1 的函数,之前用 autolisp 写过一些,现在接着写。

split-at 函数

split-at函数接受 2 个参数,1 个 list 和 1 个整数 n,它把 list 分为 2 个 list,1 个是元素索引大于等于 n 的, 剩下的元素组成另一个 list。

比如,(split-at '(a b c d e f g h) 3) => ((a b c)(d e f g h)),它在 scheme 中的实现是返回 2 个值, autolisp 不支持返回多值,所以就写成 2 个子 list。

1
2
3
(defun split-at (lst i)
  ;; 作者:徐工, 微博:@徐工徐工2020,头条:@徐工徐工
  (list (take i lst) (drop i lst)))

上面这个写法,用到了之前实现的 takedrop 函数,有兴趣的朋友可以看看之前的 blog

span 函数

span 函数也是用作分割list的,它接受2参数,1个函数符合f,1个list。它按从左到右的顺序,查找 (f x) 为真值的 list 元素, 直到 (f x) 不是真值为止,按照这个标准将list分割为2个list。

比如,(span 'even? '(2 18 3 10 22 9)) => ((2 18)(3 10 22 9))

1
2
3
(defun span (pred lst) 
  ;; 作者:徐工, 微博:@徐工徐工2020,头条:@徐工徐工
  (list (take-while pred lst) (drop-while pred lst)))

break 函数

break 函数与 span 函数很相似,不同点在于,break 是找不符合条件的,直到碰到符合条件的为止。

比如,(break 'even? '(3 1 4 1 5 9)) => ((3 1)(4 1 5 9))

1
2
3
4
5
6
7
(defun break (pred lst / y z) 
  ;; 作者:徐工, 微博:@徐工徐工2020,头条:@徐工徐工
  (setq y lst)
  (while (not (apply pred (list (car y)))) 
    (setq z (cons (car y) z)
          y (cdr y)))
  (list (reverse z) y))

fold 函数

fold 函数相当于其他编程语言里边的reduce函数,功能大同小异,主要是名称不同而已。 fold 接受 3 个参数,1个函数符号f, 1个值a,1个list,它从左到右依次用list的每个元素x来求取 (f x a) 的值,把每次取得的值赋给a,然后, 用新的a和list的下一个元素一起传给f求值,直到到达list末尾,返回最后1次的 (f x a)的值。

与其他的函数式编程语言里边的类似函数相比,SRFI 1fold 函数具有不同的行为,它按照 (f x a) 方式求值, clojurehaskell 是 按照 (f a x) 的方式求值。

我在这里给出符合 clojurehaskell 的autolisp实现。

1
2
3
4
5
(defun fold (fun2 acc lst) 
  ;; 作者:徐工, 微博:@徐工徐工2020,头条:@徐工徐工
  (if (null lst) 
    acc
    (fold fun2 (apply fun2 (list acc (car lst))) (cdr lst))))

fold-right

fold-rightfold 函数的区别在于,它是从右往左依次拿list的元素去求值的。

1
2
3
(defun fold-right (fun22 accu ls)
  ;; 作者:徐工, 微博:@徐工徐工2020,头条:@徐工徐工
  (fold fun22 accu (reverse ls)))

后记

SRFI 1的函数有几十个,到目前为止这几篇blog只给出了十来个函数的 autolisp 实现,其余的之所以没有做出来, 是因为那些主要是各种判断函数,再就是一些具有副作用的函数变种。