Architecture 朱莉娅:用许多不同但相关的算法选择构造代码
我正在寻找一种优雅的方式来重新安排我的代码。对于开发解算器,您可以有许多具有相同设置的不同选项。例如,在较高级别上,代码如下所示:Architecture 朱莉娅:用许多不同但相关的算法选择构造代码,architecture,structure,julia,Architecture,Structure,Julia,我正在寻找一种优雅的方式来重新安排我的代码。对于开发解算器,您可以有许多具有相同设置的不同选项。例如,在较高级别上,代码如下所示: function solver() # Start by assigning a bunch of variables, preprocessing, etc. ... # Choose and execute solve if alg==1 doAlgorithm1() elseif alg==2 doAlgorithm2() elseif
function solver()
# Start by assigning a bunch of variables, preprocessing, etc.
...
# Choose and execute solve
if alg==1 doAlgorithm1()
elseif alg==2 doAlgorithm2()
elseif alg==3 doAlgorithm3()
end
# Postprocess and return
...
end
以前,当我快速创建原型时,我将解算器算法正确地放入代码中。然而,随着我对越来越多的算法进行标记,这变得越来越混乱(特别是当一些算法有数百行代码时),因此我想将这些调用作为一个单独的函数。然而,我希望它们本质上与将代码块放在那里是一样的:访问相同的范围,产生副作用,等等
我曾想过使用宏来实现这一点,但由于它们在全局范围内进行评估,因此似乎是错误的解决方案。嵌套函数似乎是可行的,但我必须在解算器顶部定义它们(我的目的是不这样做以保持高级算法的可读性),并且在嵌套函数中确定嵌套函数的范围存在问题(对于仅在某些算法中重复的部分!)。我可以将其定义为另一个函数,而不尝试保持相同的作用域,但如果有长的跟踪参数(每个算法都有相同的参数),则会很糟糕
组织这种代码的好方法是什么?对于这个问题是否有一种更为儒略的方法?方法是将解算器函数作为参数传递给解算函数:
solver1(state) = "Solver 1 with state $state"
function solve(solver)
# set up the state here, e.g. in a State object
state = [1, 2]
result = solver(state)
end
solve(solver1)
“访问同一范围”与传递包含所需局部状态的变量相同。“具有效果”与从解算器方法传回变量相同
如果解算器函数足够简单,编译器会将它们内联到solve函数中,就像您直接键入它们一样(如果您担心函数调用的开销)
编辑:读得不够仔细。您提到的“参数的长轨迹”可以存储到一个特殊类型中,例如
type SolverParams
a::Int
b::Float64
params::Vector{Float64}
end
然后,每个解算器接受这种类型的参数。或者它可能只是一个传递到解算器中的元组。由于julia函数可以修改其参数,因此通常可以通过修改函数的任何参数来处理副作用 此演示使用匿名函数,允许您的解算器根据需要获取不同的参数。我不确定这是否正是你要问的,但如果你还不知道这一点,它可能会提供信息
using Base.Test
function solver1(data, initialize::Bool)
if initialize
fill!(data, 0)
end
return 1
end
function solver2(data, hellostring)
println(hellostring)
return 2
end
function solver(f)
data = [1,2,3,4]
ret = f(data)
println(sum(data))
return ret
end
@test solver(data->solver1(data, false)) == 1
@test solver(data->solver1(data, true)) == 1
@test solver(data->solver2(data, "hello, world")) == 2
它生成输出
10
0
hello, world
10
我不确定这是否比使用状态对象更好,但您可以使用宏来实现您想要的:
macro f()
quote
b = 5
x = a * b
x # the value of a block is equal to its last statement
end
end
function foo()
a = 2
b = 3
x = @f()
x, b # (10,3)
end
请注意,Julia
会自动用唯一的名称替换宏中的b
和x
,以避免副作用。如果希望产生副作用,可以使用以下方法:
macro g()
esc(quote
b = 5
x = a * b
end)
end
function bar()
a = 2
b = 3
@g()
x, b # (10,5)
end
这相当于用quote
和end
之间的代码替换@g()
。还可以定义一个小的方便宏:
macro def(name, definition)
return quote
macro $(esc(name))()
esc($(Expr(:quote, definition)))
end
end
end
因此,g
可以定义为
@def g begin
b = 5
x = a*b
end
此结果类似于将所有变量传递到
方法
,只是首先将它们置于状态
对象中。这很有效,使用Parameters.jl打包/解包state对象很容易,但我希望找到一种方法,使方法
的范围正好是求解
的范围,让它在后台完成所有传递。David,当你说“传递解算器名称”时,我认为需要明确的是,您的意思不是以字符串的形式传递名称,而是一种可以发送的类型,对吗?另外,如果SolverParams
在创建后没有更改,那么最好将设置为不可变的
,而不是类型
。我的意思是只传递函数名。我会在电脑前编辑我的答案。这不是我想要的,但是谢谢你的回复。取而代之的是,我正在寻找类似Tim posted的东西。基本上,如何复制粘贴存储在其他地方的代码(在解析时),但要比实际复制粘贴更优雅。我想另一种说法是,我从不希望doAlgorithm1()。20个特性之后,总体算法就不可能被读取了,如果我把它们放进函数中,每个算法都有50个参数要传递。我喜欢它,还有方便的宏。我只是在我的代码上试过,效果很好。