Макрос Julia @evalpoly с varargs
Я пытаюсь использовать макрос Julia @evalpoly
. Он работает, когда я поставляю коэффициенты вручную, но мне не удалось решить, как их обеспечить с помощью массива
julia> VERSION
v"0.3.5"
julia> @evalpoly 0.5 1 2 3 4
3.25
julia> c = [1, 2, 3, 4]
4-element Array{Int64,1}:
1
2
3
4
julia> @evalpoly 0.5 c
ERROR: BoundsError()
julia> @evalpoly 0.5 c...
ERROR: BoundsError()
julia> @evalpoly(0.5, c...)
ERROR: BoundsError()
Может ли кто-нибудь указать мне в правильном направлении?
Добавлен после просмотра замечательных ответов на этот вопрос
Есть одна тонкость, которую я не видел, пока не сыграл с некоторыми из этих ответов. Аргумент z
для @evalpoly
может быть переменной, но ожидается, что коэффициенты будут литералами
julia> z = 0.5
0.5
julia> @evalpoly z 1 2 3 4
3.25
julia> @evalpoly z c[1] c[2] c[3] c[4]
ERROR: c not defined
Глядя на вывод расширения этой последней команды, можно видеть, что действительно, что z назначается переменной в расширении, но что коэффициенты вставляются буквально в код.
julia> macroexpand(:@evalpoly z c[1] c[2] c[3] c[4])
:(if Base.Math.isa(z,Base.Math.Complex)
#291#t = z
#292#x = Base.Math.real(#291#t)
#293#y = Base.Math.imag(#291#t)
#294#r = Base.Math.+(#292#x,#292#x)
#295#s = Base.Math.+(Base.Math.*(#292#x,#292#x),Base.Math.*(#293#y,#293#y))
#296#a2 = c[4]
#297#a1 = Base.Math.+(c[3],Base.Math.*(#294#r,#296#a2))
#298#a0 = Base.Math.+(Base.Math.-(c[2],Base.Math.*(#295#s,#296#a2)),Base.Math.*(#294#r,#297#a1))
Base.Math.+(Base.Math.*(#298#a0,#291#t),Base.Math.-(c[1],Base.Math.*(#295#s,#297#a1)))
else
#299#t = z
Base.Math.+(Base.Math.c[1],Base.Math.*(#299#t,Base.Math.+(Base.Math.c[2],Base.Math.*(#299#t,Base.Math.+(Base.Math.c[3],Base.Math.*(#299#t,Base.Math.c[4]))))))
end)
Ответы
Ответ 1
Я не считаю, что то, что вы пытаетесь сделать, возможно, потому что @evalpoly
- это макрос, что означает, что он генерирует код во время компиляции. То, что он генерирует, является очень эффективной реализацией метода Хорнера (в случае с реальным числом), но для этого ему необходимо знать степень полинома. Длина c
не известна во время компиляции, поэтому она не работает (и не может), тогда как когда вы предоставляете коэффициенты напрямую, у нее есть все, что ей нужно.
Сообщение об ошибке не очень хорошо, поэтому, если вы можете, вы можете указать проблему на странице Julia Github?
UPDATE: в ответ на обновление вопроса, да, первым аргументом может быть переменная. Вы можете думать об этом так:
function dostuff()
z = 0.0
# Do some stuff to z
# Time to evaluate a polynomial!
y = @evalpoly z 1 2 3 4
return y
end
становится
function dostuff()
z = 0.0
# Do some stuff to z
# Time to evaluate a polynomial!
y = z + 2z^2 + 3z^3 + 4z^4
return y
end
кроме этого, а не потому, что он использует правило Хорнерса, но что угодно. Проблема в том, что он не может генерировать это выражение в время компиляции, не зная количества коэффициентов. Но не нужно знать, что такое z
.
Ответ 2
Макросы в Julia применяются к их аргументам. Чтобы выполнить эту работу, вам необходимо убедиться, что c
будет расширен до того, как будет оценен @evalpoly
. Это работает:
function f()
c=[1,2,3,4]
@eval @evalpoly 0.5 $(c...)
end
Здесь @eval
оценивает свой аргумент и расширяет $(c...)
. Позже @evalpoly
видит пять аргументов.
Как написано, это, вероятно, неэффективно, так как @eval
вызывается каждый раз, когда вызывается функция f
. Вам нужно переместить вызов на @eval
вне определения функции:
c=[1,2,3,4]
@eval begin
function f()
@evalpoly 0.5 $(c...)
end
end
Это вызывает @eval
, когда f
определен. Очевидно, что c
должно быть известно в это время. Когда f
фактически вызывается, c
больше не используется; он используется только тогда, когда определяется f
.
Ответ 3
Эрик и Иэн проделали большую работу, объясняя, почему @evalpoly
не работает и как принуждать его к работе. Однако, если вы просто хотите оценить многочлен, самое простое решение, вероятно, просто использовать Polynomials.jl:
julia> using Polynomials
c = [1,2,3,4]
polyval(Poly(c), 0.5)
3.25