带有可变参数的 Julia @evalpoly 宏
Julia @evalpoly macro with 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()
有人能指出我正确的方向吗?
看到这个问题的精彩答案后添加
有一个微妙之处是我在尝试其中一些答案之前没有发现的。 @evalpoly
的 z
参数可以是一个变量,但系数应该是文字
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)
我不相信你正在尝试做的事情是可能的,因为 @evalpoly
是一个宏——这意味着它在编译时生成代码。它生成的是 Horner 方法的一个非常有效的实现(在实数情况下),但要这样做它需要知道多项式的次数。 c
的长度在编译时是未知的,所以它不(也不能)工作,而当你直接提供系数时,它拥有它需要的一切。
尽管错误消息不是很好,所以如果可以的话,您可以在 Julia Github 页面上提出问题?
更新:针对问题的更新,是的,第一个参数可以是一个变量。你可以这样想:
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
除了,不是那个,因为它使用 Horners 规则,但无论如何。问题是,它无法在 编译时间 不知道系数的数量的情况下生成该表达式。但它根本不需要知道 z
是什么。
Julia 中的宏应用于它们的参数。要完成这项工作,您需要确保 c
在计算 @evalpoly
之前展开。这有效:
function f()
c=[1,2,3,4]
@eval @evalpoly 0.5 $(c...)
end
此处,@eval
计算其参数,并展开 $(c...)
。后来,@evalpoly
看到五个参数。
如所写,这可能效率不高,因为每次调用函数 f
时都会调用 @eval
。您需要将对 @eval
的调用移到函数定义之外:
c=[1,2,3,4]
@eval begin
function f()
@evalpoly 0.5 $(c...)
end
end
定义 f
时调用 @eval
。很显然,此时必须知道c
。每当实际调用 f
时,不再使用 c
;它仅在定义 f
时使用。
Erik 和 Iain 在解释为什么 @evalpoly
不起作用以及如何强制它起作用方面做得很好。但是,如果您只想计算多项式,最简单的解决方案可能就是使用 Polynomials.jl:
julia> using Polynomials
c = [1,2,3,4]
polyval(Poly(c), 0.5)
3.25
我正在尝试使用 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()
有人能指出我正确的方向吗?
看到这个问题的精彩答案后添加
有一个微妙之处是我在尝试其中一些答案之前没有发现的。 @evalpoly
的 z
参数可以是一个变量,但系数应该是文字
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)
我不相信你正在尝试做的事情是可能的,因为 @evalpoly
是一个宏——这意味着它在编译时生成代码。它生成的是 Horner 方法的一个非常有效的实现(在实数情况下),但要这样做它需要知道多项式的次数。 c
的长度在编译时是未知的,所以它不(也不能)工作,而当你直接提供系数时,它拥有它需要的一切。
尽管错误消息不是很好,所以如果可以的话,您可以在 Julia Github 页面上提出问题?
更新:针对问题的更新,是的,第一个参数可以是一个变量。你可以这样想:
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
除了,不是那个,因为它使用 Horners 规则,但无论如何。问题是,它无法在 编译时间 不知道系数的数量的情况下生成该表达式。但它根本不需要知道 z
是什么。
Julia 中的宏应用于它们的参数。要完成这项工作,您需要确保 c
在计算 @evalpoly
之前展开。这有效:
function f()
c=[1,2,3,4]
@eval @evalpoly 0.5 $(c...)
end
此处,@eval
计算其参数,并展开 $(c...)
。后来,@evalpoly
看到五个参数。
如所写,这可能效率不高,因为每次调用函数 f
时都会调用 @eval
。您需要将对 @eval
的调用移到函数定义之外:
c=[1,2,3,4]
@eval begin
function f()
@evalpoly 0.5 $(c...)
end
end
定义 f
时调用 @eval
。很显然,此时必须知道c
。每当实际调用 f
时,不再使用 c
;它仅在定义 f
时使用。
Erik 和 Iain 在解释为什么 @evalpoly
不起作用以及如何强制它起作用方面做得很好。但是,如果您只想计算多项式,最简单的解决方案可能就是使用 Polynomials.jl:
julia> using Polynomials
c = [1,2,3,4]
polyval(Poly(c), 0.5)
3.25