首页 > 解决方案 > 如何将变量传递给宏并在宏执行之前对其进行评估?

问题描述

如果我有方法

macro doarray(arr)
    if in(:head, fieldnames(typeof(arr))) && arr.head == :vect
        println("A Vector")
    else
        throw(ArgumentError("$(arr) should be a vector"))
    end
end

如果我写这个就可以了

@doarray([x])

或者

@doarray([:x])

但是下面的代码正确地不起作用,提高了ArgumentError(ie ArgumentError: alist should be a vector)。

alist = [:x]
@doarray(alist)

我怎样才能使上述行为类似于 @doarray([x])

动机:我有一个递归宏(比如mymacro),它接受一个向量,对第一个值进行操作,然后mymacro用向量的其余部分递归调用(比如rest_vector)。我可以创建rest_vector,正确打印值(用于调试),但我不知道如何评估rest_vector当我再次将它提供给它mymacro时​​。

编辑 1:我正在尝试在 Julia 中实现逻辑编程,即 MiniKanren。在我基于此的 Clojure 实现中,代码就是这样。

(defmacro fresh
  [var-vec & clauses]
  (if (empty? var-vec)
    `(lconj+ ~@clauses)
    `(call-fresh (fn [~(first var-vec)]
                   (fresh [~@(rest var-vec)]
                     ~@clauses)))))

基于此的我失败的 Julia 代码如下。如果它没有意义,我深表歉意,因为我试图通过实现它来理解宏。

macro fresh(varvec, clauses...)
    if isempty(varvec.args)
        :(lconjplus($(esc(clauses))))
    else
        varvecrest = varvec.args[2:end]
        return quote
            fn = $(esc(varvec.args[1])) -> @fresh($(varvecvest), $(esc(clauses)))
            callfresh(fn)
        end
    end
end

运行代码时出现的错误@fresh([x, y], ===(x, 42))(您可以忽略===(x, 42)此讨论)

ERROR: LoadError: LoadError: UndefVarError: varvecvest not defined

问题线是fn = $(esc(varvec.args[1])) -> @fresh($(varvecvest), $(esc(clauses)))

标签: julia

解决方案


如果我正确理解您的问题,最好在宏内部调用一个函数(不是宏),该函数将对传递给宏的 AST 进行操作。这是一个简单的示例,您可以如何做到这一点:

function recarray(arr)
    println("head: ", popfirst!(arr.args))
    isempty(arr.args) || recarray(arr)
end

macro doarray(arr)
    if in(:head, fieldnames(typeof(arr))) && arr.head == :vect
        println("A Vector")
        recarray(arr)
    else
        throw(ArgumentError("$(arr) should be a vector"))
    end
end

当然,在这个例子中,我们没有做任何有用的事情。如果您指定了您想要实现的确切目标,那么我可能会提出更具体的建议。


推荐阅读