“setf”中“f”的本意

大多数人第一眼见到setf,可能会简单的把f理解成form。这种解释很符合直觉,因为我们已经有了对symbol进行赋值操作的setq。而setf可以对form(广义变量)进行赋值。

然而这个直觉是错误的。 Deutsch在他的论文《A Lisp machine with very compact programs》中(原文)提到

A more useful function is (SETFQ (fn arg1 ... argn) newvalue)
which quotes the function name and evaluates everything else. 
This allows RPLACA, for example, to be defined as (LAMBDA (X Y) (SETFQ (CAR X) Y) ).

这里面提到的rplaca在Elisp里叫做setcar

所以setfsetfq的缩写,f的意思是function。 另外在这篇论文中我们也可以看到MicroLisp引入了现在我们使用的广义变量的概念

参考:https://g000001.cddddr.org/3715857294

6 个赞

这里面的 setfq 底层是个正儿八经的 function,通过 cdr coding 直接修改 reference ,而不是像后来的 setf 用 macro expand 实现的。一来是因为 BBN LISP 那会还只有 FEXPR 没有 macro,二来 cdr coding 不是 portable 的,对实现要求限制比较大

3 个赞

这个功能有点玄乎,感觉好像在修改右值

let flet 为什么不是letf

我一直以为是field的意思

不知道,顺带一提事实上Emacs里的cl-letf在CL并没有对应的letf存在,是Emacs自己加的扩展

(setf FORM VALUE)可以理解成“使FORM eval的结果为VALUE”

Elisp里还有这种黑魔法

ELISP> (macroexpand-all '(setf (eq a 3) t))
(cond
  (t
   (setq a 3))
  ((eq a 3)
   (setq a
         (not 3))))

setf只用过一次, 发现修改了原list的元素, 会影响到其他变量, 所以setf之前先拷贝一下

本来就是不纯的操作,和其他语言修改struct的slot一样,struct的引用不变,内容变了。不过elisp有很多纯的列表操作,让你产生了错觉罢了

(defun replace-at (idx elem list)
  (let ((tail (nthcdr (1+ idx) list))
        head)
    (dotimes (i idx)
      (push (pop list) head))
    (nconc (nreverse head) (cons elem tail))))

写一个纯的替换列表元素

没看懂,这里发生了什么?

我靠,感觉这个有点反人类直觉。。。。。

eval一下不就知道了?

我觉得这样理解很合理呢 :joy: