【问题标题】:Expanding macro results in KeyError扩展宏导致 KeyError
【发布时间】:2021-12-27 07:38:14
【问题描述】:

使用以下(简化的)代码:

(setv agnostic-manager-installers {})

(defmacro alias-assign [am &rest aliases]
    (for [alias aliases] (assoc
        agnostic-manager-installers
        (str alias)
        (-> (globals) (get (str am)) (get "install")))))

(setv brew {
    "prefix" "/home/linuxbrew/.linuxbrew/"
    "install" (defn brew [] (run
                "curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh | sudo bash"
                :shell True))
})

(alias-assign brew brew homebrew home-brew linuxbrew linux-brew)

我收到以下错误:

Traceback (most recent call last):
  File "/home/shadowrylander/.local/syvl/python/hy/bin/hy", line 8, in <module>
    sys.exit(hy_main())
  File "/usr/lib/python3.9/contextlib.py", line 137, in __exit__
    self.gen.throw(typ, value, traceback)
  File "<stdin>", line 9, in alias_assign
hy.errors.HyMacroExpansionError: 
  File "<stdin>", line 20
    (alias-assign brew brew homebrew home-brew linuxbrew linux-brew)
    ^--------------------------------------------------------------^
expanding macro alias-assign
  KeyError: 'brew'

我认为宏不应该在编译时评估参数,如果我正确读取错误(我认为我不是)?基本上,我想在提供给alias-assign 的每个别名周围写上双引号,这就是我使用宏的原因。

【问题讨论】:

    标签: hy


    【解决方案1】:

    这里有一些更简单的代码会产生同样的错误:

    (setv brew 1)
    
    (defmacro m []
      (get (globals) "brew"))
    
    (m)
    

    也许问题现在更加明显:尝试在宏扩展期间访问全局变量 brew 不起作用,因为在发生宏扩展时,brew 在编译期间不存在。如果您只说brew 而不是(get (globals) "brew"),则会出现同样的问题,使用NameError 而不是KeyError。在任何情况下,(setv brew 1) 表单在运行时才会被评估。解决这个问题的一种方法是改用(eval-when-compile (setv brew 1))。这使得评估发生得更早。

    一个更广泛的问题是,由于宏扩展,您似乎正在执行实际上想要返回的代码。毕竟,宏的主体是for,所以它总是会返回None。对比以下代码,它使用引用和取消引用来生成表单并返回它们(并使用更新的语法):

    (setv agnostic-manager-installers {})
    
    (defmacro alias-assign [am #* aliases]
      `(do ~@(gfor
        alias aliases
        `(setv (get agnostic-manager-installers ~(str alias))
          (get ~am "install")))))
    
    (setv brew (dict
      :prefix "/home/linuxbrew/.linuxbrew/"
      :install "placeholder"))
    
    (alias-assign brew brew homebrew home-brew linuxbrew linux-brew)
    
    (print (hy.repr agnostic-manager-installers))
    

    结果是:

    {"brew" "placeholder"  "homebrew" "placeholder"  "home-brew" "placeholder"  "linuxbrew" "placeholder"  "linux-brew" "placeholder"}
    

    【讨论】:

    • 1.使用assoc 与使用setv 将安装函数分配给宏中的字典有什么区别? 2. 我实际上并没有尝试在宏中返回任何内容;我可以做? 3、为什么新的宏定义中没有使用&amp;rest
    • @ShadowRylander 1. 什么都没有。 2. 是的。如果这让您感到惊讶,您可能应该阅读一两个关于宏的教程(任何 Lisp 都可以用于学习基础知识)。 3. &amp;rest 自 1.0a1 以来一直是 #*。这就是我所说的“更新语法”的意思。
    • 知道了;再次感谢,会做的!
    猜你喜欢
    • 2017-02-26
    • 2021-09-14
    • 2015-04-19
    • 1970-01-01
    • 1970-01-01
    • 2014-05-06
    • 1970-01-01
    • 2019-09-15
    • 2012-08-01
    相关资源
    最近更新 更多