Julia中惯用的OOP方法,类型和方法

TJB*_*TJB 6 julia

在学习Julia的过程中,我想知道如何正确地完成以前可能在Python,Java或C ++中所做的事情。例如,以前我可能使用抽象基类(或接口)来通过类定义一系列模型。每个类可能都具有类似的方法calculate。所以可以这样称呼我model.calculate(),其中model是来自继承类之一的对象。

我知道Julia使用多个分派来重载具有不同签名的函数,例如calculate(model)。我的问题是如何创建不同model的。我是否为此使用类型系统并创建不同的类型,例如:

abstract type Model end
type BlackScholes <: Model end
type Heston <: Model end
Run Code Online (Sandbox Code Playgroud)

哪里BlackScholesHeston不同类型的model?如果是这样,那么我可以重载不同的calculate方法:

function calculate(model::BlackScholes)
    # code
end

function calculate(model::Heston)
    # code
end
Run Code Online (Sandbox Code Playgroud)

但是我不确定这是否是Julia中类型的正确习惯用法。我将非常感谢您的指导!

Col*_*ers 9

这是一个很难回答的问题。Julia提供了广泛的工具来解决任何给定的问题,即使是语言的核心开发人员也很难断言一种特定的方法是“正确的”甚至是“惯用的”方法。

例如,在模拟和求解随机微分方程的领域中,您可以查看Chris Rackauckas(及其他许多人)在JuliaDiffEq框架下的软件包套件中采用的方法。但是,其中许多人都是经验丰富的Julia编码人员,对于那些经验不足的Julia编码人员而言,他们所做的工作可能有点遥不可及,他们只是想以某种合理的方式实现某种事物的建模,而这仅仅是凡人。

这个问题的唯一“正确”答案可能是将用户定向到文档的“ 性能提示”部分,然后断言只要您不违反那里的任何建议,那么您在做什么可能还可以。

我认为,我可以根据自己的亲身经历回答此问题的最佳方法是提供一个示例(我只是一个凡人)将如何处理模拟不同的Ito过程的问题。尽管增加了一层,但实际上与您提出的问题相差不远。明确地说,我没有断言这是做事的“正确”方法,而只是说这是一种以合理合理的方式利用多重调度和Julia类型系统的方法。

我从抽象类型开始,用于嵌套代表特定模型的特定子类型。

abstract type ItoProcess ; end
Run Code Online (Sandbox Code Playgroud)

现在,我定义一些特定的模型子类型,例如

struct GeometricBrownianMotion <: ItoProcess
    mu::Float64
    sigma::Float64
end
struct Heston <: ItoProcess
    mu::Float64
    kappa::Float64
    theta::Float64
    xi::Float64
end 
Run Code Online (Sandbox Code Playgroud)

请注意,在这种情况下,我不需要添加将参数转换为的构造函数Float64,因为Julia会自动执行此操作,例如GeometricBrownianMotion(1, 2.0)可以即用即用,因为Julia 在构造类型时会自动转换11.0

但是,我可能想为常见的参数化添加一些构造函数,例如

GeometricBrownianMotion() = GeometricBrownianMotion(0.0, 1.0)
Run Code Online (Sandbox Code Playgroud)

我可能还需要一些函数来返回有关模型的有用信息,例如

number_parameter(model::GeometricBrownianMotion) = 2
number_parameter(model::Heston) = 4
Run Code Online (Sandbox Code Playgroud)

实际上,考虑到我如何定义上述模型,我实际上可能会有点偷偷摸摸,并定义一种适用于所有子类型的方法:

number_parameter(model::T) where {T<:ItoProcess} = length(fieldnames(typeof(model)))
Run Code Online (Sandbox Code Playgroud)

现在,我想添加一些代码来模拟我的模型:

function simulate(model::T, numobs::Int, stval) where {T<:ItoProcess}
   # code here that is common to all subtypes of ItoProcess
   simulate_inner(model, somethingelse)
   # maybe more code that is common to all subtypes of ItoProcess
end
function simulate_inner(model::GeometricBrownianMotion, somethingelse)
   # code here that is specific to GeometricBrownianMotion
end
function simulate_inner(model::Heston, somethingelse)
   # code here that is specific to Heston
end
Run Code Online (Sandbox Code Playgroud)

请注意,我已经使用了抽象类型,让我把所有的代码是共同的所有亚型ItoProcess中的simulate功能。然后,我使用多个分派,并simulate_inner运行需要特定于的特定子类型的任何代码ItoProcess。由于上述原因,我犹豫使用短语“惯用语”,但是让我说,以上内容是典型的Julia代码中的一种非常常见的模式。

上面的代码中要注意的一件事是确保simulate函数的输出类型是类型稳定的,即,输出类型可以由输入类型唯一地确定。类型稳定性通常是确保高性能Julia代码的重要因素。在这种情况下,确保类型稳定性的一种简单方法是始终返回Matrix{Float64}(如果输出类型对于所有子类型都是固定的,ItoProcess那么显然是唯一确定的)。在下面的estimate示例中,我将检查输出类型取决于输入类型的情况。无论如何,因为simulate我可能总是会回来,Matrix{Float64}因为GeometricBrownianMotion我只需要一个列,但是因为Heston我将需要两个列(第一个用于资产价格,第二个用于波动率过程)。

实际上,取决于代码的使用方式,高性能代码并不一定总是需要类型稳定性(例如,参见使用函数屏障来防止类型不稳定性流到程序的其他部分),但这是一个好习惯在(用于Julia代码)。

我可能还希望例程估计这些模型。同样,我可以遵循相同的方法(但要稍作改动):

function estimate(modeltype::Type{T}, data)::T where {T<:ItoProcess}
    # again, code common to all subtypes of ItoProcess
    estimate_inner(modeltype, data)
    # more common code
    return T(some stuff generated from function that can be used to construct T)
end
function estimate_inner(modeltype::Type{GeometricBrownianMotion}, data)
    # code specific to GeometricBrownianMotion
end
function estimate_inner(modeltype::Type{Heston}, data)
    # code specific to Heston
end
Run Code Online (Sandbox Code Playgroud)

simulate案例有一些区别。我没有输入GeometricBrownianMotionor 的实例Heston,而是输入了类型本身。这是因为我实际上不需要为字段定义值的类型的实例。实际上,这些字段的值正是我试图估计的!但是我仍然想使用多重调度,因此是::Type{T}构造。另请注意,我已经为指定了输出类型estimate。此输出类型取决于::Type{T}输入,因此该函数是类型稳定的(输出类型可以由输入类型唯一地确定)。但是,在这种simulate情况下很常见,我已经对代码进行了结构化,因此ItoProcess只需要编写一次所有子类型通用的代码,然后分离出特定于这些子类型的代码。

这个答案变成了一篇论文,所以我在这里把它绑起来。希望这对OP以及进入Julia的其他人都是有用的。最后,我想强调的是,我上面所做的只是一种方法,还有其他方法也可以起到同样的作用,但是我个人发现上述方法从结构的角度来看是有用的,并且在整个过程中也相当普遍。朱莉娅生态系统。