使用let-bound变量渗透`set-process-sentinel`层次结构

时间:2014-07-03 08:22:11

标签: emacs elisp lexical-scope

我从来没有能够提出一种方法来穿透set-process-sentinel层次结构,其中包含函数开头定义的let-bound变量 - 只有缓冲区本地或全局变量可以穿透它。允许绑定变量可以达到第一个start-process,但这是因为无法识别而无法被拒绝 - 在函数开始时定义的let-bound变量 似乎无法穿透以(lambda (p e) . . .开头的部分。任何人都可以想到一种方法,包括穿透嵌套哨兵,如下例所示?

  
(set-process-sentinel 
  (start-process
    "my-process-name-one"
     "*OUTPUT-BUFFER*"
    "/path/to/executable"
    "argument-one"
    "argument-two"
    "argument-three")
  (lambda (p e) (when (= 0 (process-exit-status p))
    (set-process-sentinel 
      (start-process
        "my-process-name-two"
        nil ;; example of not using an output buffer
        "/path/to/executable"
        "argument-one"
        "argument-two"
        "argument-three")
      (lambda (p e) (when (= 0 (process-exit-status p))
        (set-process-sentinel 
          (start-process . . . ))))))))

3 个答案:

答案 0 :(得分:7)

问题是Emacs Lisp变量绑定默认是动态的。也就是说,在评估函数时,在定义函数的环境中,但在调用函数的环境中,查找绑定变量 not

Emacs 24或更高版本本身支持词法绑定(即函数看到函数 definition 中绑定的变量),但由于它改变了现有代码的语义,因此需要启用它明确。通常,这是通过将文件局部变量设置添加到.el文件的第一行来完成的:

;; -*- lexical-binding: t; -*-

另一种方法是使用lexical-let库中的cl。这也适用于早期的Emacs版本。请注意,通过这种方式,您可以明确指定哪些变量应该具有词法绑定,因此(lexical-let ((foo foo)) ...)之类的代码并不罕见 - foo是一个现有的变量,需要进行“转移”。进入功能。

答案 1 :(得分:2)

以下是使用动态绑定的示例:

  
(defun example-dynamic-fn ()
"Doc-string"
(interactive)
  (let ((test-variable "Hello-world!"))
    (set-process-sentinel
      (start-process "process-one" "*one*" "echo" test-variable)
      `(lambda (p e) (when (= 0 (process-exit-status p))
        (set-process-sentinel
          (start-process "process-two" "*two*" "echo" ,test-variable)
          '(lambda (p e) (when (= 0 (process-exit-status p))
            (start-process "process-three" "*three*" "echo" ,test-variable)
            (set-process-sentinel
              (start-process "process-four" "*four*" "echo" ,test-variable)
              '(lambda (p e) (when (= 0 (process-exit-status p))
                (set-process-sentinel
                  (start-process "process-five" "*five*" "echo" ,test-variable)
                  '(lambda (p e) (when (= 0 (process-exit-status p))
                    (message "test-variable:  %s" ,test-variable)))))))))))))))

答案 2 :(得分:1)

好的,我想我现在已经有了这个。上面的链接提供了一个很好的例子;如果有其他人遇到这种困难,还有另一个:

;;; ensure VAR1 has no binding
(makunbound 'VAR1)
;;;
(defun f1 (&optional VAR1)
  (interactive)
  (unless VAR1
    (set 'VAR1 "variable1"))
  (pop-to-buffer "*test*")
;  (lexical-let ( (VAR1 VAR1) ) ;;;
    (set-process-sentinel
     (start-process-shell-command "test"
                  "*test*"
                  (concat "echo " VAR1))
     (lambda (process event)
       (condition-case err
       (when (string-match-p "finished" event)
         (f2 VAR1))
     (error
      (princ
       (format "Sentinel error: %s" err))))))
;    ) ;;;
  )
;;;
(defun f2 (&optional VAR2)
   (interactive)
  (unless VAR2
    (set 'VAR2 "VARIABLE2"))
  (print VAR2))

我们加载所有内容((f1)注释掉的行)并运行(f1)。在发生错误之前,VAR1的值会传递给(f2) 。错误(void-variable VAR1)似乎来自(set-process sentinel PROCESS SENTINEL)的范围环境; VAR1未定义SENTINEL,即使它仍在(lambda)(set ))函数的范围内。

如果变量只是要使函数具有局部范围,那么同样使用;并不是最佳实践。

如果我们取消注释标有(set-process sentinel )的行,那么一切都按预期工作。令人高兴的是,我们可以将值传递给另一个函数,这会阻止一系列SENTINEL的长期构建。如果需要,它还允许我们使用其他子流程生成流程。

我的一个错误是将(lexical-let )命名为离散函数,而不是将其保留在lexical-binding: t;函数中。虽然(let )方法很有吸引力,但它往往会破坏依赖于标准{{1}}的工作代码。