在 Julia 中将公式作为函数参数传递
Pass a formula as a function parameter in Julia
我正在尝试创建一个允许更改 Julia 中的公式和系数的函数。我 80% 确定我应该为此使用匿名函数?
This SO post using python 是我试图完成的一个更离散的例子(特别是 chepner 的基本 python 例子,而不是使用库) . Pass a formula as a function parameter in python
我还发现这个 SO post 使用 Julia,它使用一种类型来存储所需的参数,然后将它们传递给一个函数。
以这些为基础,这是我到目前为止创建的:
#Create composite type
type Params
formula
b1::Float64
b2::Float64
end
#create instance of type and load
foo=Params((b1,b2,X)-> X^b1+X+b2,0.004,0.005)
#create function
function DoMath(X,p::Params)
p.formula(X,varargs...) #??
end
关于如何使用复合类型 and/or lambda 来构造它,我是否走在正确的轨道上?我没有接受过任何 CS 培训,在尝试学习 Julia 时,我对许多概念都一头雾水。
允许用户更改公式和任何系数的函数的正确语法是什么。对于给定的 X?最终,我想象的是具有以下功能的东西:
DoMath(4) #some default formula with changing X
DoMath(4, X*b1 +X*b2) #change X and change formula
DoMath(4, (X,b1,b2,b3)->X*b1+X*b2+x*b3) # change x, change formula to a 3 parameter function
谢谢
更新:
我按照@Chris 的语法让它工作。我不得不修补的一件事是使用
(p::Params)(x) = p.formula(x,p.b) #p.b, not just b otherwise error
而且我必须在调用
之前将 2.0 和 3.0 包装在一个数组中
p = Params((x,b)->x*b[1]+b[2],[2.0,3.0])
想法是构建一个可调用类型。可调用类型是任何具有 "call" 的类型。函数 f
是可调用类型,因为您可以调用它:例如 f(x)
。然而,函数并不是唯一可以像函数一样工作的东西。实际上,在 Julia 中,函数基本上是 <: Function
.
的可调用类型
那么让我们为您的示例构建一个。让你的类型包含你想要的数据:
type Params
b1::Float64
b2::Float64
end
现在让我们添加对 Params
的调用。假设我们想要做 x*b1 + b2
。我们可以通过以下方式进行调用:
(p::Params)(x) = x*p.b1 + p.b2
让我们看看它是如何工作的。制作一个参数:
p = Params(2.0,3.0)
现在我们可以使用它的调用来计算公式:
p(4) # 4*2+3 = 11
现在看到 p
作为一个使用内部数据的函数。就是这个。
其余的都是在同一个基础上建立起来的。您需要尊重 Julia 类型不是动态的这一事实。这是有充分理由的。但是,假设您不知道想要多少个 b。然后你可以让一个字段成为 b
的数组:
type Params
b::Vector{Float64}
end
(p::Params)(x) = x*b[1] + b[2]
现在假设您希望能够修改公式。然后你可以有一个公式字段:
type Params
formula
b::Vector{Float64}
end
并使调用将值放入其中:
(p::Params)(x) = p.formula(x,b)
现在如果用户这样做了:
p = Params((x,b)->x*b[1]+b[2],2.0,3.0)
然后,和以前一样:
p(4) # 4*2+3 = 11
是的,它的行为是一样的,并且仍然使用内部数据。
但是由于 p
只是任何类型,我们可以修改字段。所以这里我们可以修改:
p.formula = (x,b)-> x*b[1]+x*b[2]+b[3]
push!(p.b,2.0) # p.b == [2.0,3.0,2.0]
然后再次调用,现在使用更新的字段:
p(4) # 4*2 + 4*3 + 2 = 22
确实,正如@LyndonWhite 指出的那样,ParameterizedFunctions.jl 实现了类似的功能。这样做的策略是可调用类型。
额外的细节
某些库的构建(错误地)要求用户传入一个函数。所以这里我们有一个 p
"acts like a function",但有些图书馆不会接受它。
但是,有一个快速修复方法。就让它 <:Function
。示例:
type Params <: Function
b1::Float64
b2::Float64
end
现在,需要功能的东西将把您的 p
当作 <:Function
。这只是指出在 Julia 中,Function
是一个抽象类型,每个 function
只是一个子类型 Function
.
的可调用类型
这是我目前用于处理这些 "fixed parameters" 与 "changing parameters" 问题的类似模式。
固定参数是那些在 运行 特定程序时不经常更改的参数(例如 b1
、b2
、b3
)。更改参数是公共变量(例如 x
),它们几乎在每次函数调用之间不断变化。在许多情况下,使用可选参数或关键字参数足以解决问题,但如果我们想同时更改函数及其参数,这种解决方案可能不是理想的解决方案。正如 中的答案所建议的那样,更好的方法是创建一个类型并使用多重分派。但是,我们还需要手动 unpack
函数体中的类型。事实上,我们可以使用 @generated
宏编写一个更通用的函数 wapper:
abstract FormulaModel
immutable Foo{F<:Function} <: FormulaModel
formula2fixedparams::F
fixedParam1::Float64
fixedParam2::Float64
end
Foo(f, b1=5., b2=10.) = Foo(f, b1, b2)
Foo() = Foo((x,b1,b2)->x^b1+x+b2) # default formula
immutable Bar{F<:Function} <: FormulaModel
formula3fixedparams::F
fixed1::Float64
fixed2::Float64
fixed3::Float64
end
Bar(b1,b2,b3) = Bar((x,b1,b2,b3)->b1*x+b2*x+b3*x, b1, b2, b3)
Bar() = Bar(1.,2.,3.)
@generated function DoMath(model::FormulaModel, changingParams...)
fixedParams = [:(getfield(model, $i)) for i = 2:nfields(model)]
func = :(getfield(model, 1))
# prepare some stuff
quote
# do something
$func(changingParams..., $(fixedParams...))
# do something else
end
end
julia> DoMath(Foo(), 4)
1038.0
julia> DoMath(Foo((x,y,b1,b2)->(b1*x+b2*y)), 4, 10)
120.0
julia> DoMath(Bar(), 4)
24.0
我正在尝试创建一个允许更改 Julia 中的公式和系数的函数。我 80% 确定我应该为此使用匿名函数?
This SO post using python 是我试图完成的一个更离散的例子(特别是 chepner 的基本 python 例子,而不是使用库) . Pass a formula as a function parameter in python
我还发现这个 SO post 使用 Julia,它使用一种类型来存储所需的参数,然后将它们传递给一个函数。
以这些为基础,这是我到目前为止创建的:
#Create composite type
type Params
formula
b1::Float64
b2::Float64
end
#create instance of type and load
foo=Params((b1,b2,X)-> X^b1+X+b2,0.004,0.005)
#create function
function DoMath(X,p::Params)
p.formula(X,varargs...) #??
end
关于如何使用复合类型 and/or lambda 来构造它,我是否走在正确的轨道上?我没有接受过任何 CS 培训,在尝试学习 Julia 时,我对许多概念都一头雾水。
允许用户更改公式和任何系数的函数的正确语法是什么。对于给定的 X?最终,我想象的是具有以下功能的东西:
DoMath(4) #some default formula with changing X DoMath(4, X*b1 +X*b2) #change X and change formula DoMath(4, (X,b1,b2,b3)->X*b1+X*b2+x*b3) # change x, change formula to a 3 parameter function
谢谢
更新: 我按照@Chris 的语法让它工作。我不得不修补的一件事是使用
(p::Params)(x) = p.formula(x,p.b) #p.b, not just b otherwise error
而且我必须在调用
之前将 2.0 和 3.0 包装在一个数组中 p = Params((x,b)->x*b[1]+b[2],[2.0,3.0])
想法是构建一个可调用类型。可调用类型是任何具有 "call" 的类型。函数 f
是可调用类型,因为您可以调用它:例如 f(x)
。然而,函数并不是唯一可以像函数一样工作的东西。实际上,在 Julia 中,函数基本上是 <: Function
.
那么让我们为您的示例构建一个。让你的类型包含你想要的数据:
type Params
b1::Float64
b2::Float64
end
现在让我们添加对 Params
的调用。假设我们想要做 x*b1 + b2
。我们可以通过以下方式进行调用:
(p::Params)(x) = x*p.b1 + p.b2
让我们看看它是如何工作的。制作一个参数:
p = Params(2.0,3.0)
现在我们可以使用它的调用来计算公式:
p(4) # 4*2+3 = 11
现在看到 p
作为一个使用内部数据的函数。就是这个。
其余的都是在同一个基础上建立起来的。您需要尊重 Julia 类型不是动态的这一事实。这是有充分理由的。但是,假设您不知道想要多少个 b。然后你可以让一个字段成为 b
的数组:
type Params
b::Vector{Float64}
end
(p::Params)(x) = x*b[1] + b[2]
现在假设您希望能够修改公式。然后你可以有一个公式字段:
type Params
formula
b::Vector{Float64}
end
并使调用将值放入其中:
(p::Params)(x) = p.formula(x,b)
现在如果用户这样做了:
p = Params((x,b)->x*b[1]+b[2],2.0,3.0)
然后,和以前一样:
p(4) # 4*2+3 = 11
是的,它的行为是一样的,并且仍然使用内部数据。
但是由于 p
只是任何类型,我们可以修改字段。所以这里我们可以修改:
p.formula = (x,b)-> x*b[1]+x*b[2]+b[3]
push!(p.b,2.0) # p.b == [2.0,3.0,2.0]
然后再次调用,现在使用更新的字段:
p(4) # 4*2 + 4*3 + 2 = 22
确实,正如@LyndonWhite 指出的那样,ParameterizedFunctions.jl 实现了类似的功能。这样做的策略是可调用类型。
额外的细节
某些库的构建(错误地)要求用户传入一个函数。所以这里我们有一个 p
"acts like a function",但有些图书馆不会接受它。
但是,有一个快速修复方法。就让它 <:Function
。示例:
type Params <: Function
b1::Float64
b2::Float64
end
现在,需要功能的东西将把您的 p
当作 <:Function
。这只是指出在 Julia 中,Function
是一个抽象类型,每个 function
只是一个子类型 Function
.
这是我目前用于处理这些 "fixed parameters" 与 "changing parameters" 问题的类似模式。
固定参数是那些在 运行 特定程序时不经常更改的参数(例如 b1
、b2
、b3
)。更改参数是公共变量(例如 x
),它们几乎在每次函数调用之间不断变化。在许多情况下,使用可选参数或关键字参数足以解决问题,但如果我们想同时更改函数及其参数,这种解决方案可能不是理想的解决方案。正如 unpack
函数体中的类型。事实上,我们可以使用 @generated
宏编写一个更通用的函数 wapper:
abstract FormulaModel
immutable Foo{F<:Function} <: FormulaModel
formula2fixedparams::F
fixedParam1::Float64
fixedParam2::Float64
end
Foo(f, b1=5., b2=10.) = Foo(f, b1, b2)
Foo() = Foo((x,b1,b2)->x^b1+x+b2) # default formula
immutable Bar{F<:Function} <: FormulaModel
formula3fixedparams::F
fixed1::Float64
fixed2::Float64
fixed3::Float64
end
Bar(b1,b2,b3) = Bar((x,b1,b2,b3)->b1*x+b2*x+b3*x, b1, b2, b3)
Bar() = Bar(1.,2.,3.)
@generated function DoMath(model::FormulaModel, changingParams...)
fixedParams = [:(getfield(model, $i)) for i = 2:nfields(model)]
func = :(getfield(model, 1))
# prepare some stuff
quote
# do something
$func(changingParams..., $(fixedParams...))
# do something else
end
end
julia> DoMath(Foo(), 4)
1038.0
julia> DoMath(Foo((x,y,b1,b2)->(b1*x+b2*y)), 4, 10)
120.0
julia> DoMath(Bar(), 4)
24.0