Специализированные вызовы методов в порядке в метапрограммировании - PullRequest
2 голосов
/ 10 мая 2019

У меня проблема после вызова макроса:

@introspectable square(x) = x * x

Тогда при звонке квадрат (3) я должен быть в состоянии получить 9, потому что вызов функции был специализирован для выполнения атрибута структуры, который является кодом Джулии, однако, когда я вхожу в макрос, код, кажется, оценивается напрямую.

Что я пробовал:

struct IntrospectableFunction
  name
  parameters
  native_function
end


(f::IntrospectableFunction)(x) = f.native_function(x)

macro introspectable(expr)
  name = expr.args[1].args[1]
  parameters = tuple(expr.args[1].args[2:end]...)
  body = expr.args[2].args[2]

:( global $name  = IntrospectableFunction( :( name ), $parameters, :( body ) ))
end

@introspectable square(x) = x * x
square(3)

Ответ должен быть 9, однако я получаю «Объект типа символ не может быть вызван». Однако если я заменю :( body) на x -> x * x, я получу желаемый результат, моя цель - обобщить макро-вызов.

1 Ответ

3 голосов
/ 11 мая 2019

Мне обычно легче работать с выражениями в макросах (это не самый короткий способ писать вещи, но, по моему опыту, намного легче контролировать то, что генерируется).

Поэтому я бы переписал ваш код как:

macro introspectable(expr)
    name = expr.args[1].args[1]
    parameters = expr.args[1].args[2:end]
    anon = Expr(Symbol("->"), Expr(:tuple, parameters...), expr.args[2].args[2])
    constr = Expr(:call, :IntrospectableFunction, QuoteNode(name), Tuple(parameters), anon)
    esc(Expr(:global, Expr(Symbol("="), name, constr)))
end

Теперь, когда вы сказали, что хотите обобщить, я бы определил ваш функтор так:

(f::IntrospectableFunction)(x...) = f.native_function(x...)

(таким образом вы разрешаете передавать несколько позиционных аргументов).

Теперь давайте проверим наши определения:

julia> @introspectable square(x) = x * x
IntrospectableFunction(:square, (:x,), getfield(Main, Symbol("##3#4"))())

julia> square(3)
9

julia> @macroexpand @introspectable square(x) = x * x
:(global square = IntrospectableFunction(:square, (:x,), ((x,)->x * x)))

julia> @introspectable toarray(x,y) = [x,y]
IntrospectableFunction(:toarray, (:x, :y), getfield(Main, Symbol("##5#6"))())

julia> toarray("a", 10)
2-element Array{Any,1}:
   "a"
 10

julia> @macroexpand @introspectable toarray(x,y) = [x,y]
:(global toarray = IntrospectableFunction(:toarray, (:x, :y), ((x, y)->[x, y])))

julia> function localscopetest()
       @introspectable globalfun(x...) = x
       end
localscopetest (generic function with 1 method)

julia> localscopetest()
IntrospectableFunction(:globalfun, (:(x...),), getfield(Main, Symbol("##9#10"))())

julia> globalfun(1,2,3,4,5)
(1, 2, 3, 4, 5)

julia> function f()
       v = 100
       @introspectable localbinding(x) = (v, x)
       end
f (generic function with 1 method)

julia> f()
IntrospectableFunction(:localbinding, (:x,), getfield(Main, Symbol("##11#12")){Int64}(100))

julia> localbinding("x")
(100, "x")

(обратите внимание, что полезно использовать @macroexpand, чтобы убедиться, что наш макрос работает должным образом)

РЕДАКТИРОВАТЬ - как обрабатывать минимальную множественную отправку

Я пишу не-макрос пример, потому что он связан со структурой данных:

Используйте, например, такое определение:

struct IntrospectableFunction
  name::Symbol
  method_array::Vector{Pair{Type{<:Tuple}, Function}}
end

function (f::IntrospectableFunction)(x...)
    for m in f.method_array
        if typeof(x) <: first(m)
            return last(m)(x...)
        end
    end
    error("signature not found")
end

и теперь вы можете написать:

julia> square = IntrospectableFunction(:square, [Tuple{Any}=>x->x*x,Tuple{Any,Any}=>(x,y)->x*y])
IntrospectableFunction(:square, Pair{DataType,Function}[Tuple{Any}=>##9#11(), Tuple{Any,Any}=>##10#12()])

julia> square(3)
9

julia> square(2,3)
6

Имейте в виду, что изложенный мной подход не идеален и универсален - он просто служит очень простым примером того, как вы могли бы это сделать.

...