метапрограммирование джулии и оценка переменных nloops - PullRequest
0 голосов
/ 29 октября 2018

Я новичок в метапрограммировании, так что, возможно, я не понимаю этого. Я думал, что цель макроса @nloops в Base.Cartesian состоит в том, чтобы сделать возможным кодирование произвольного числа вложенных циклов в условиях, когда размерность априори неизвестна. В документации к модулю приведен следующий пример:

@nloops 3 i A begin
    s += @nref 3 A i
end

, что оценивается как

for i_3 = 1:size(A,3)
    for i_2 = 1:size(A,2)
        for i_1 = 1:size(A,1)
            s += A[i_1,i_2,i_3]
        end
    end
end

Здесь число 3 известно априори. Однако для моих целей и для целей, которые, как я думал, был создан nloops, число вложенных уровней заранее неизвестно. Поэтому я не смог бы жестко закодировать целое число 3. Даже в документации указано:

(основной) синтаксис @nloops выглядит следующим образом:

  • Первый аргумент должен быть целым числом (а не переменной), определяющим количество циклов.

...

Если я назначу целочисленное значение - скажем, размерность массива, который передается в функцию - какой-то переменной, макрос nloops больше не работает:

b = 3
@nloops b i A begin
    s += @nref b A i
end

Возвращает ошибку:

ERROR: LoadError: MethodError: no method matching _nloops(::Symbol, ::Symbol, ::Symbol, ::Expr)
Closest candidates are:
  _nloops(::Int64, ::Symbol, ::Symbol, ::Expr...) at cartesian.jl:43
...

Я не знаю, как nloops оценивает переменную b как целое число, а не как символ. Я просмотрел документацию и попробовал различные итерации eval и других функций и макросов, но он либо интерпретируется как символ, либо Expr. Как правильно, юлианский способ написать это?

1 Ответ

0 голосов
/ 29 октября 2018

См. количество выражений :

julia> A = rand(4, 4, 3)  # 3D array (Array{Int, 3})

Генерируемая функция похожа на макрос, в которой результирующее выражение не возвращается, но компилируется и выполняется при вызове / вызове, также видит тип (и их введите параметры курса) аргументов, то есть:

  • внутри сгенерированной функции, A - это Array{T, N}, , а не значение массива.
  • значит T равно Int и N равно 3!

Здесь внутри выражения в кавычках N интерполируется в выражение с синтаксисом $N, который оценивается как 3:

julia> @generated function mysum(A::Array{T,N}) where {T,N}
           quote
               s = zero(T)
               @nloops $N i A begin
                   s += @nref $N A i
               end
               s
           end
       end
mysum (generic function with 1 method)

julia> mysum(A)
23.2791638775186

Вы можете сконструировать выражение, а затем оценить его, т.е.

julia> s = 0; n = 3;

julia> _3loops = quote
           @nloops $n i A begin
               global s += @nref $n A i
           end
       end
quote
    @nloops 3 i A begin
        global s += @nref(3, A, i)
    end
end

julia> eval(_3loops)

julia> s
23.2791638775186

Я вычистил вручную LineNumberNode s из AST для удобства чтения (есть также MacroTools.prettify, который делает это за вас).

Для запуска этого примера в REPL требуется для объявления s как global внутри цикла в Julia 1.0.

...