请教关于let函数作用域的问题

emacs.d/init.el at 7ce37fbcf598daaeda11645e1ae6bac2fe22b5c7 · purcell/emacs.d · GitHub 22行

(let ((normal-gc-cons-threshold (* 20 1024 1024))
      (init-gc-cons-threshold (* 128 1024 1024)))
  (setq gc-cons-threshold init-gc-cons-threshold)
  (add-hook 'emacs-startup-hook
            (lambda () (setq gc-cons-threshold normal-gc-cons-threshold))))

我猜测是 normal-gc-cons-threshold 这个变量的作用域是在let内,lambda函数运行的时候,已经不在let的作用域了,所以报错了?请指教

;; -*- lexical-binding: t -*-
(add-hook 'emacs-startup-hook
            (lambda () (setq gc-cons-threshold normal-gc-cons-threshold)))

做的只有把(lambda () (setq gc-cons-threshold normal-gc-cons-threshold))这个函数加入emacs-startup-hook,然后你的let binding就结束了。当Emacs在之后运行emacs-start-up-hook的时候(backtrack里的run-hooks xxx),自然找不到这个变量。

;; -*- lexical-binding: t -*-声明使用lexical-binding的之后,lambda返回的函数自动变成closure,closure会记录lexical环境,包括你的normal-gc-cons-threshold

3 个赞

如前面的回复所说,你要像 purcell 那样开启 Lexical Binding,之后这段代码才会有效。

(setq lexical-binding t)
;; => t

(let ((normal-gc-cons-threshold (* 20 1024 1024))
      (init-gc-cons-threshold (* 128 1024 1024)))
  (setq gc-cons-threshold init-gc-cons-threshold)
  (add-hook 'emacs-startup-hook
            (lambda () (setq gc-cons-threshold normal-gc-cons-threshold))))
;; => ((closure ((init-gc-cons-threshold . 134217728) (normal-gc-cons-threshold . 20971520) t) nil (setq gc-cons-threshold normal-gc-cons-threshold)))

开启 Lexical Binding 之后,(lambda ...) 的返回值变成了 (info “(elisp) Closures”),不再是普通的 (lambda ...) 列表了。

1 个赞

说起closure我正好有个问题想问你……

怎么把这种带closure的代码改为不需要lexical-binding的形式?

为什么?现在默认都应该用 Lexical Binding(推荐)。

回到你的问题,自然就需要放弃 Lexical Binding 提供的特性了,比如这样一个函数

;; -*- lexical-binding: t; -*-
(let ((x 123))
  (lambda ()
    x))

就得改成

;; -*- lexical-binding: nil; -*-
(lambda ()
  (let ((x 123))
    x))

或者

;; -*- lexical-binding: nil; -*-
(defvar x 123)
(lambda ()
  x)
2 个赞

感谢

洁癖……至少在这个用注释开启的方法被改变之前我是尽量避免使用的

(setq-default lexical-binding t)不行吗?我这里没问题

Python 也有类似的(Magic Comment),而且支持 Emacs 的写法:

#!/usr/bin/python
# -*- coding: utf-8; -*-
print("世界,你好!")

惊了,原来这个写法的源头是Emacs吗

这个写法的源头似乎真的是emacs的file-local variable

1970 年 ITS 上的 TECO Emacs 就支持 -*- -*- 了。之后几乎所有 Emacs 版本都支持。

2 个赞

谢谢各位,了解啦:hugs: