lisp序列处理的理解与疑惑

在ruby中对一个序列处理,大部分是函数式写法。连续串接多个序列处理,写起来也特别的便捷。给开发者的感觉,ruby提倡序列的处理用函数式写法来完成。

而在lisp中,有do,dotimes,dolist, for,while还有cl-loop 这些迭代式写法,也有mapcar这种函数式的写法,我就迷惑了。

迭代式写法

(dolist (c '("a" "e" "k" "u"))
  (let ((key (kbd (concat "C-" c))))
    (evil-define-key 'insert vterm-mode-map key
      `(lambda () (interactive) (vterm-send-string ,key)))))

函数式写法

(mapcar (lambda (c)
          (let ((key (kbd (concat "C-" c))))
            (evil-define-key 'insert vterm-mode-map key
              `(lambda () (interactive) (vterm-send-string ,key)))))
         '("a" "e" "k" "u"))

lisp到底推荐那种,什么情况下用哪种比较好?

1 个赞

以代码清晰可维护为主

人有左手和右手,所以手冲到底要用哪只手?

1 个赞
(dolist (key '([f1] [f2] [f3] [f4] [f5] [f6] [f7] [f8] [f9] [f10] [f11] [f12]))
     (define-key vterm-mode-map `,key nil))

这个写法,我想改成(dotimes (n 12) body)的写法,没有成功,能帮改一下吗?不是洁癖的问题,而是为了练习迭代和`这个宏

基于(define-key vterm-mode-map [f1] nil))是正确的写法,改修如下,但不对

(dotimes (n 12)
     (let ((key (kbd (concat "f" (int-to-string (1+ n))))))
          (define-key vterm-mode-map [`,key] nil)))

[]代表数组,那么f1,f2…f12就应该是某个变量呀,C-h v却找不到它们。 那么 (define-key vterm-mode-map [f1] nil))为什么又是正确的呢?

因为vector字面量具有self-quoting效果

(equal '[3 a] [3 a]) ;; => t
(equal [a b c] (vector 'a 'b 'c)) ;; => t
;; 不能用`eq',因为eq比较两个symbol的内存地址是否一致。

[f1]里面装的也不是字符串,是symbol。要把string转化为symbol可以用intern string都会有双引号包裹的

(equal [f1] (vector (intern "f1"))) ;; => t
1 个赞

好的,明白了,还有self-quoting这个概念,学习了。已收藏。

eq和equal的区别,这个我清楚。

intern这个函数,我倒是知道,因为前面不知道有self-quoting的概念,所以也没联系起来。

另外扩展一下,常用的有self-quoting功能的是哪些?

受教了!谢谢!

勉強なりました!ありがとうございます!

(equal "Emacs" '"Emacs")
(equal 3 '3)
(equal :keyword ':keyword)
(equal ?a '?a)

均为t

1 个赞

除了 list 和 symbol 其它所有元素都是 self quoting

keyword 算constant symbol的

我并没有说 keyword 就不会 self quote 了,除此以外还有 t 和 nil,或者其实可以理解为,keyword 只是 intern 的时候自动把值设为本身,并不算 self quoting,因为对于 string, number 来说 symbol-value 并没有作用。

我理解的还是不到位

(dolist (key '([f1] [f2] [f3] [f4] [f5] [f6] [f7] [f8] [f9] [f10] [f11] [f12]))
     (define-key vterm-mode-map `,key nil))

改成下面的写法也不对

(dotimes (n 12)
         (let ((key (intern (concat "f" (int-to-string (1+ n))))))
           (define-key vterm-mode-map [`,key] nil)))

[`,key],[]是self-quoting的,在这里`,key返回的也是个quote,所以失败。但我又不清楚怎么改成正确的。 :frowning:

You know why? Do you even think this would work?

(let ((a 12)) `"b,a") ;; => "b12"

dotimes dolist cl-loop都是宏,都是用 while 实现的,编译过后它们之间常常没差别,本质上跟你直接用 while 一样。dolist 更多时候跟 mapc 相互替换,都是仅为了副作用。

开启 lexical-binding 的话,用不着执行里面的 key,lexical 环境下执行 lambda 会生成 closure (可以存储环境变量的 lambda):

(car
 (mapcar (lambda (c)
           (let ((key (kbd (concat "C-" c))))
             (lambda () (message "=> %S" key))))
         '("a" "e" "k" "u")))
;; => (closure ((key . "^A") (c . "a") t) nil (message "=> %S" key))

我说怎么没scheme方便呢?原来没开启词法作用域。过会儿我试一下

因为[]已经阻断了Emacs对`reader macro的解析。你应该用`[,key]

太好了,又学了一点。

素晴らしい、もう一点を勉強になりました。

楼主的这些疑问,找一本lisp入门书过一遍效果会比较好,推荐 A gentle introduction to symbolic computation 和practical Common Lisp 的前几章

好的,书也在看,不过最近才开始,所以会遇到还没看到的知识点。

现在看的是ANSI Common Lisp,但内容不是很多,很多东西和scheme接近。先看完这个再说

你可能会喜欢

(mapc (lambda (x)
               (--> x
                    (concat "C-" it)
                    (kbd it)
                    (evil-define-key 'insert vterm-mode-map it
                                     (lambda () (interactive)
                                       (vterm-send-string it)))))
             '("a" "e" "k" "u"))
(defmacro --> (x &rest forms)
  "Starting with the value of X, thread each expression through FORMS.

Insert X at the position signified by the symbol `it' in the first
form.  If there are more forms, insert the first form at the position
signified by `it' in in second form, etc."
  (declare (debug (form body)))
  `(-as-> ,x it ,@forms))

(defmacro -as-> (value variable &rest forms)
  "Starting with VALUE, thread VARIABLE through FORMS.

In the first form, bind VARIABLE to VALUE.  In the second form, bind
VARIABLE to the result of the first form, and so forth."
  (declare (debug (form symbolp body)))
  (if (null forms)
      `,value
    `(let ((,variable ,value))
       (-as-> ,(if (symbolp (car forms))
                 (list (car forms) variable)
               (car forms))
            ,variable
              ,@(cdr forms)))))

From dash.el

1 个赞

挪一下`的位置,就OK了。:grinning:

(dotimes (n 12)
         (let ((key (intern (concat "f" (int-to-string (1+ n))))))
           (define-key vterm-mode-map `[,key] nil)))

这个写法太赞了,你的例子在最后一句,我小改了一下

(mapc (lambda (x)
        (--> x
             (concat "C-" it)
             (kbd it)
             (evil-define-key 'insert vterm-mode-map it 
                 `(lambda () (interactive) (vterm-send-string ,it)))))
      '("a" "e" "k" "u"))

这个和ruby的思路接近,但过程不一样

ruby是前一个序列处理结束之后,把结果传递给下一个序列处理,每个步骤的结果都是一个序列。

这里是先利用–>宏是把每个元素串接处理,再利用mapc把结果形成一个序列。

但ruby的方法,也有其灵活性,比如 listobj.map(…).filter(…).map(…),也就是,ruby的方式在串接处理的时候,可以改变序列的长度。

而–>宏就做不到了,需要在mapc外层加filter了。总之,我还是比较喜欢–>这个宏。