Julia @evalpoly macro with varargs

2019-04-19 12:20发布

I'm trying to grok using Julia's @evalpoly macro. It works when I supply the coefficients manually, but I've been unable to puzzle out how to provide these via an array

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()

Can someone point me in the right direction on this?

Added after seeing the great answers to this question

There is one subtlety to that I hadn't seen until I played with some of these answers. The z argument to @evalpoly can be a variable, but the coefficients are expected to be literals

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

Looking at the output of the expansion of this last command, one can see that it is indeed the case that z is assigned to a variable in the expansion but that the coefficients are inserted literally into the code.

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)

标签: julia
3条回答
何必那么认真
2楼-- · 2019-04-19 12:42

I don't believe what you are trying to do is possible, because @evalpoly is a macro - that means it generates code at compile-time. What it is generating is a very efficient implementation of Horner's method (in the real number case), but to do so it needs to know the degree of the polynomial. The length of c isn't known at compile time, so it doesn't (and cannot) work, whereas when you provide the coefficients directly it has everything it needs.

The error message isn't very good though, so if you can, you could file an issue on the Julia Github page?

UPDATE: In response to the update to the question, yes, the first argument can be a variable. You can think of it like this:

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

is becoming

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

except, not that, because its using Horners rule, but whatever. The problem is, it can't generate that expression at compile time without knowing the number of coefficients. But it doesn't need to know what z is at all.

查看更多
时光不老,我们不散
3楼-- · 2019-04-19 12:51

Erik and Iain have done a great job of explaining why @evalpoly doesn't work and how to coerce it into working. If you just want to evaluate the polynomial, however, the easiest solution is probably just to use Polynomials.jl:

julia> using Polynomials
       c = [1,2,3,4]
       polyval(Poly(c), 0.5)
3.25
查看更多
倾城 Initia
4楼-- · 2019-04-19 12:54

Macros in Julia are applied to their arguments. To make this work, you need to ensure that c is expanded before @evalpoly is evaluated. This works:

function f()
    c=[1,2,3,4]
    @eval @evalpoly 0.5 $(c...)
end

Here, @eval evaluates its argument, and expands $(c...). Later, @evalpoly sees five arguments.

As written, this is probably not efficient since @eval is called every time the function f is called. You need to move the call to @eval outside the function definition:

c=[1,2,3,4]
@eval begin
    function f()
        @evalpoly 0.5 $(c...)
    end
end

This calls @eval when f is defined. Obviously, c must be known at this time. Whenever f is actually called, c is not used any more; it is only used while f is being defined.

查看更多
登录 后发表回答