使用许多没有成员函数的子案例编写干净的 Julia 代码
我正在尝试 Julia 语言(来自 Python 背景),并且对解决使用 Python 中的对象解决的问题的最自然方法感兴趣。
基本上,我正在尝试编写一个函数,该函数将评估一些简单的一维数学基础,然后将它们组合起来近似一个多维函数。在 Python 中,我会写一些类似的东西
basis_1d_values = scheme_1d.evaluate(points)
对于我的每个scheme_1d对象。我会使用父 Scheme1d 类,然后有各种类型的一维方案(线性、切比雪夫多项式等)的子类,它们知道如何运行它们单独的“评估”函数。
在 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 只能在 1 个对象上调度(选择一个方法),因此您可以将其设置为自己的类型(类),该类型(类)拥有自己的 版本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)
朱莉娅同时调度scheme_1d和points(因此是多重调度),所以evaluate不属于任何类型。相反,它是具有多个方法的1 个函数,每个方法通过其组合输入类型进行区分。
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,但您确实应该查找教程以获取更详细的信息。在您学习时,请记住以下几点会有所帮助:
- 另一个很大的区别是在 Julia 中,超类型是抽象的,你不能用它们创建一个实例。您可以从中创建实例的类型称为“具体”。
- Julia 对特定具体输入类型的编译称为专业化。这与方法调度不同,而是在它之后发生。这意味着您可以编写带有抽象参数的方法(在上面的示例中,
points从未指定类型,因此它被隐式地赋予通用抽象类型Any),并且 Julia 将为每个合适的具体类型进行多种特化。 - Specializations 似乎是一个看不见的实现细节,但我认为它有助于理解 Julia 的编译如何工作,所以我会告诉你包的
methodinstances功能MethodAnalysis是当前查看这些的首选方式。