6

我从来没有想出一种方法来使用在函数开头定义的set-process-sentinellet-bound 变量来穿透层次结构——只有缓冲区局部变量或全局变量才能穿透它。let-bound 变量可以到达 first ,但这是它们可以穿透而不会由于无法识别而被拒绝的范围 -在函数开头定义的let-bound 变量似乎无法穿透开始的部分与. 谁能想到一种方法来做到这一点,包括像下面的示例中那样穿透嵌套的哨兵?start-process(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 . . . ))))))))
4

3 回答 3

7

问题是 Emacs Lisp 变量绑定默认是动态的。也就是说,在计算函数时,绑定变量不是在定义函数的环境中查找,而是在调用函数的环境中查找。

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

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

另一种选择是lexical-letcl库中使用。这也适用于早期的 Emacs 版本。请注意,通过这种方式,您可以明确指定哪些变量应该具有词法绑定,因此诸如此类的代码(lexical-let ((foo foo)) ...)并不少见——foo是一个需要“转移”到函数中的现有变量。

于 2014-07-03T09:14:31.583 回答
3

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

(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)))))))))))))))
于 2017-03-18T21:17:23.130 回答
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)VAR1SENTINEL(lambda)

(set )当变量仅具有函数的本地范围时,如上所述使用也不是最佳实践。

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

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

于 2015-02-24T04:04:47.283 回答