编写干净的Julia代码,包含许多不带成员函数的子案例



我正在尝试Julia语言(来自Python背景),并且对解决我在Python中使用对象可以解决的问题的最自然的方法感兴趣。

基本上,我正在尝试编写一个函数,该函数将评估一些简单的1-d数学基础,然后将它们组合起来以近似多维函数。在Python中,我会写像

这样的东西basis_1d_values = scheme_1d.evaluate(points)

对应每个scheme_1d对象。我将使用父类Scheme1d,然后为各种类型的1-d模式(线性,Chebyshev多项式等)创建子类,这些子类将知道如何运行它们单独的evaluate"功能。

在Julia中最自然的方法是什么?显然,像

这样的长if语句
if scheme_1d_type == "linear"
basis_1d_values = evaluate_linear(scheme_1d, points)
elif scheme_1d_type == "chebyshev"
basis_1d_values = evaluate_chebyshev(scheme_1d, points)
elif ...
...
end

将工作,但它是非常笨拙的,因为我将需要使用这些分支if语句和单独的函数,每次子类的行为不同(并将不得不跟踪每一个if语句,当我以某种方式更新代码)。如有任何建议,不胜感激。

谢谢!

您讨论的是多重分派和子类型,这是Julia非常基础和特有的,任何入门教程都会涉及到。我不打算全部解释,因为它们做得更好,但既然你是专门来自Python的,我可以指出一些基本的类比。

Python只能在一个对象上调度(选择一个方法),所以你让它成为自己的类型(类),它拥有自己的evaluate版本,甚至在函数调用期间将它写在点之前,只是为了指出它有多特别。

class Scheme1D:
pass
class Linear(Scheme1D):
def evaluate(self, points):
return "linear"
class Chebyshev(Scheme1D):
def evaluate(self, points):
return "chebyshev"
points = None
scheme_1d = Linear()
scheme_1d.evaluate(points)
# Python checks for type(scheme_1d) to find the correct evaluate
# and runs its bytecode (which was compiled upon class definition)

Julia同时在scheme_1dpoints上调度(因此是多重调度),因此evaluate不属于任何类型。相反,它是一个具有多个方法的函数,每个方法通过其组合的输入类型来区分。

abstract type Scheme1D end
struct Linear <: Scheme1D
end
struct Chebyshev <: Scheme1D
end
# first method definition also creates the function
function evaluate(x::Linear, points)
return "linear"
end
# adds a method to the function
function evaluate(x::Chebyshev, points)
return "chebyshev"
end
points = nothing
scheme_1d = Linear()
evaluate(scheme_1d, points)
# Julia checks the types of scheme_1d and points, finds the
# most fitting method, compiles the method for those types
# if it is the first call, and runs the method

这应该可以帮助你适应Julia,但你应该查阅教程了解更详细的细节。还有几件事在你学习的时候会对你有帮助:

  1. 另一个很大的区别是,在Julia中,超类型是抽象的,你不能用它们创建一个实例。可以用来创建实例的类型被称为"具体类型"。
  2. Julia对特定具体输入类型的编译称为专门化。这与方法分派不同,但发生在方法分派之后。这意味着你可以写一个带有抽象参数的方法(在上面的例子中,points从来没有指定类型,所以它隐式地给出了通用抽象类型Any),并且Julia将为每个合适的具体类型进行多个专门化。
  3. 专门化似乎是一个看不见的实现细节,但我认为它对理解Julia的编译工作是有用的,所以我告诉你MethodAnalysis包的methodinstances函数是当前查看这些的方法。

我不确定我完全理解了,但它与Python中没有太大的不同:您可以使用抽象类型Scheme_1d,然后创建特定的线性,chebyshev等方案作为子类型。

那么,您将拥有一个基于方案类型进行分派的evaluate_scheme(scheme,points)函数(请参阅有关"多重分派"的文档)。

最新更新