【问题标题】:invoking functions defined by flet in another function在另一个函数中调用由 flet 定义的函数
【发布时间】:2011-08-01 13:06:33
【问题描述】:

我有一组在foo 中定义的函数,我还想在bar 中使用这些函数。我在foo 中定义了这些函数,因为我希望foo 是自包含的——否则我知道我可以在外部(全局)定义这些函数以供foobar 以及其他函数访问,或在labels 构造中同时定义foobar,其中这些函数仅针对foobar 定义。无论如何,我希望 foo 在没有外部函数或 labels 结构的情况下可以分发。因此面临挑战。

这是我到目前为止所拥有的(在这种情况下我使用的是 Emacs Lisp),但是当我调用 bar 时,我现在最终在 foo 中全局定义了本地函数。关于如何在bar“on the fly”中定义局部变量/函数有什么建议吗?

(defun foo (x)
  (flet ((addone (x) (1+ x))
     (addtwo (x) (+ 2 x)))
    (addtwo x)))

(defun bar (x) 
  (let* ((fnlist (car (cdaddr (symbol-function 'foo))))
     (nfn (length fnlist))
     (ifn nil)
     (bar-body '(addone x))
     (i 0))
    (eval (append 
       ;; local symbol names
       ;; to which function definitions will be set
       (list 'let (mapcar 'car fnlist))
       ;; set function definitions
       (list '(while (< i nfn)
            (setq ifn (nth i fnlist))
            (eval `(fset ',(car ifn) ,(append (list 'lambda) (cdr ifn))))
            (setq i (1+ i))))
       ;; function invocation
       (list bar-body)))))

功能应用:

(foo 1) ;;=> 3

(bar 1) ;;=> 2

(addone 1) ;;=> 2 ?should not be defined outside of bar?

【问题讨论】:

    标签: lisp elisp


    【解决方案1】:

    这不是独立的,所以它不是真正的答案;但是和你提到的其他选项不同,所以我还是会添加它。

    (defmacro with-foo-functions (&rest forms)
      `(flet ((addone (x) (1+ x))
              (addtwo (x) (+ 2 x)))
         ,@forms))
    
    (defun foo (x)
      (with-foo-functions
       (addtwo x)))
    
    (defun bar (x)
      (with-foo-functions
       (addone x)))
    

    【讨论】:

    • 宏!我需要了解这些。我一定会记住这些——谢谢!
    • 我个人会在 arglist 中使用 &amp;body forms,在模板中使用 '@forms
    • 好电话,虽然我假设您的意思是说&amp;rest forms,@forms?我已将其编辑为答案。
    • @phils:我实际上是指 &body,它在宏 lambda 列表中的作用与 &rest 相同,但允许有足够意识的编辑器推断出 body 应该有“progn-like”缩进而不是“将 args 挂在 funcall 上”缩进。我确实是说,@forms
    • 啊,我明白了。不过,Emacs lisp 是否支持这一点? elisp 信息页面中没有提到它,我无法让它代替 &amp;rest
    【解决方案2】:

    Emacs Lisp 具有动态绑定。这与几乎所有其他 Lisps 使用的词法绑定不同。例如,如果您尝试在 Common Lisp 中执行以下操作,您将收到一条错误消息,指出 FOO 未定义:

    (defun bar ()
      (foo 10))
    
    (flet ((foo (x) (1+ x)))
      (bar))
    

    然而,在 Emacs Lisp 中,由于 FOO 是动态绑定的,这将返回 11,因为 FOO 的绑定在 BAR 中可用。

    Emacs Lisp 不为函数提供词法绑定,因此为了在 Emacs Lisp 中实现相同的功能,您必须通过将 lambda 绑定到词法变量来伪造它,然后使用宏来隐藏 FUNCALL

    (lexical-let ((foo #'(lambda (x) (1+ x))))
      (macrolet ((foo (x) `(funcall foo ,x)))
        (foo 10)))
    

    此问题的另一个答案建议使用宏而不是 flet。这可行,但会导致不必要的代码重复。我的解决方案以每次要调用该函数时都必须编写macrolet 部分或使用funcall 为代价来防止这种情况。如果这是经常需要的东西,可以编写一个宏来将所有这些包装在一个词法 flet 版本中。

    【讨论】:

    • 嗨@Elias,在Emacs Lisp 中,我相信使用labels 宏允许函数的词法作用域。但是我用let 定义一个局部变量并为其分配一个lambda 表达式的方式不等同于你建议的方法,用funcallmacrolet 调用函数吗?...也许我错过了一个重要的概念联系。谢谢...
    • 在 Emacs Lisp 中,letlabelsflet 具有动态范围。这意味着如果您使用这些方法中的任何一种绑定变量,则这些变量/函数将在从其主体调用的任何函数中可用。解决方法是使用 lexical-let 伪造正确的词法绑定。这可以防止定义泄漏。 macrolet 技巧只是提供正常的调用语义,以避免必须键入 (funcall foo 10)
    • 谢谢——知道了。杰出的。但不幸的是,它并不像我最初想象的那么简单。
    • 其实你是对的。 labels 实际上是 Emacs Lisp 中的词法范围。我只是将其归因于 Emacs Lisp 中的更多不一致。
    猜你喜欢
    • 1970-01-01
    • 2018-04-10
    • 1970-01-01
    • 2017-09-01
    • 1970-01-01
    • 2011-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多