定义二维数组的陷阱

(make-vector 2 (make-vector 3 0)) 定义一个二行三列数组似乎符合直觉,但有陷阱,同样是修改 [0, 0],对比:

(let ((vec (make-vector 2 (make-vector 3 0))))
  (setf (elt (elt vec 0) 0) 100)
  vec)
;; => [[100 0 0] [100 0 0]]

(let ((vec [[0 0 0]
            [0 0 0]]))
  (setf (elt (elt vec 0) 0) 100)
  vec)
;; => [[100 0 0] [0 0 0]]

根源在于 make-vector 版本中,(elt vec 0)(elt vec 1)eq 的(同一个对象),正确的做法看起来麻烦些:

(defun my-make-two-dims-array (width height)
  (let ((vec (make-vector height nil)))
    (dotimes (i height vec)
      (setf (elt vec i) (make-vector width 0)))))

(let ((vec (my-make-two-dims-array 3 2)))
  (setf (elt (elt vec 0) 0) 100)
  vec)
;; => [[100 0 0] [0 0 0]]
2 个赞

如果 make-vector 第二个参数是 object 的话,就是指针赋值了:

DEFUN ("make-vector", Fmake_vector, Smake_vector, 2, 2, 0,
       doc: /* Return a newly created vector of length LENGTH, with each element being INIT.
See also the function `vector'.  */)
  (Lisp_Object length, Lisp_Object init)
{
  CHECK_NATNUM (length);
  struct Lisp_Vector *p = allocate_vector (XFASTINT (length));
  for (ptrdiff_t i = 0; i < XFASTINT (length); i++)
  p->contents[i] = init;
  return make_lisp_ptr (p, Lisp_Vectorlike);
 }

所以不管几维,修改的都是同一个 object。

1 个赞

嗯,make-list 也是,平时很少需要 初始化 List,一般都一个一个往里面加值,不会受此影响。Emacs Lisp 有不少和 Mutable 相关的陷阱。

那个函数有点像C语言的

(defun my-make-two-dims-array (width height)
  (apply 'vector
         (mapcar '(lambda (A)(make-vector width 0))
                 (make-string height ?A))))

Emacs Lisp里除了基本类型以外都是传递指针的吧?

那么平时注意使用这种会复制参数的函数的时候不要直接传入基本类型以外的值,应该就可以避免坑了。

Emacs Lisp 有不少和 Mutable 相关的陷阱。

还有哪些啊?

这不是什么陷阱吧,求值顺序而已。

是什么意思?

明明是 pass by reference 和 evaluation order 有啥关系。而且又不是 Scheme,众所周知 Emacs Lisp 是严格从左向右求值的。

这里有个类似 cl 的 make-array 实现:EmacsWiki: cl-array.el

2 个赞

先求(make-vector 3 0),求完当参数了。

在ELisp里,我唯一初始化过的东西就是keymap variable。别的东西都是直接手写出来……

1 个赞

那不就是 pass by reference 么,和 evaluation order 啥关系,call by value 语言参数永远要先求值,evaluation order 只说明参数之间的求值先后

1 个赞

后来想了一下,还是思维方式不一样。

我对主楼产生了这种误解的原因在于以为他觉得(make-vector 3 0)会求值两次。

而你的想法是认为他觉得会把整个vector复制下来。

争这个有什么用。。。