Макрос 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