Julia:为库设置OOP模型的最佳方法是什么

ccs*_*csv 3 oop function julia

我正在尝试创建一个库.假设我有一个模型,其中我有一个输出,输入和描述函数的等式.输入将是:

x= [1,2,3,4,5,6]
y= [5,2,4,8,9,2]
Run Code Online (Sandbox Code Playgroud)

我把它放到一个函数中:

#=returns y values=#
function fit (x,a,b)
    y=ax+b
end
Run Code Online (Sandbox Code Playgroud)

另一个使用describe函数输出摘要:

#=Describes equation fitting=#

function describe(#insert some model with data from the previous functions)
   #=Prints the following: Residuals(y-fit(y)), x and y and r^2
     a and b

     =#
end
Run Code Online (Sandbox Code Playgroud)

在朱莉娅这样做的最佳方式是什么?我应该用type吗?

目前我正在使用一个非常大的功能,例如:

function Model(x,y,a,b,describe ="yes")
    ....function fit
    ....Something with if statements to controls the outputs
    ....function describe
end
Run Code Online (Sandbox Code Playgroud)

但这不是非常有效或用户友好.

Mr *_*pha 8

看起来你正试图将一种特定的OOP风格套在朱莉娅身上,这种风格并不合适.朱莉娅没有课程.相反,您使用类型的组合,在这些类型上分派的函数以及封装整体的模块.

作为一个组成的例子,让我们做一个做OLS回归的包.为了封装代码,您将其包装在模块中.让我们称之为OLSRegression:

module OLSRegression

end
Run Code Online (Sandbox Code Playgroud)

我们需要一个模型来存储回归的结果并发送到:

type OLS
    a::Real
    b::Real
end
Run Code Online (Sandbox Code Playgroud)

然后,我们需要一个函数来使我们的OLS适合数据.fit我们可以扩展StatsBase.jl中可用的功能,而不是创建自己的功能:

using StatsBase

function StatsBase.fit(::Type{OLS}, x, y)
    a, b = linreg(x, y)
    OLS(a, b)
end
Run Code Online (Sandbox Code Playgroud)

然后我们可以创建一个describe函数来打印出拟合的模型:

function describe(obj::OLS)
    println("The model fit is y = $(obj.a) + $(obj.b) * x")
end
Run Code Online (Sandbox Code Playgroud)

最后,我们需要从模块中导出创建的类型和函数:

export OLS, describe, fit
Run Code Online (Sandbox Code Playgroud)

整个模块放在一起是:

module OLSRegression

using StatsBase

export OLS, describe, fit

type OLS <: RegressionModel
    a::Real
    b::Real
end

function StatsBase.fit(::Type{OLS}, x, y)
    a, b = linreg(x, y)
    OLS(a, b)
end

function describe(obj::OLS)
    println("The model fit is y = $(obj.a) + $(obj.b) * x")
end

end
Run Code Online (Sandbox Code Playgroud)

然后你会像这样使用它:

julia> using OLSRegression

julia> m = fit(OLS, [1,2,5,4], [2,2,4,6])

julia> describe(m)
The model fit is y = 1.1000000000000005 + 0.7999999999999999 * x
Run Code Online (Sandbox Code Playgroud)

编辑:让我添加一些关于方法,多个调度和阴影的注释.

在传统的OOP语言中,您可以使用具有相同名称的方法的不同对象.例如:我们有对象dog和对象cat.他们都有一个叫做的方法run.我可以run使用点语法调用适当的方法:dog.run()cat.run().这是单一派遣.根据第一个参数的类型调用适当的方法.由于第一个参数的重要性,它出现在方法名称之前,而不是在括号内.

在Julia这种用于调用方法的点语法,但它仍然有调度.相反,第一个参数出现在括号内,就像所有其他参数一样.所以你会做run(dog)或者run(cat)它仍然调度到适当的方法dogcat类型.

这也是正在发生的事情describe(obj::OLS).我正在创建一个新的方法描述并指定当第一个参数是类型时应该调用此方法OLS.

朱莉娅的调度超越了单一调度到多次调度.在单牒的电话cat.run("fast")cat.run(5)将分发到相同的方法和它是由做不同的事情有不同类型的第二个参数的方法.在朱莉娅run(cat, "fast")run(cat, 5)派遣分开的方法.

我见过朱莉娅的创作者称之为动词语言和传统的OOP语言.在名词语言中,您将方法附加到对象(名词),但在Julia中,您将方法附加到泛型函数(动词).在上面的模块中,我都创建了一个新的泛型函数describe(因为没有该名称的泛型函数)并在其上附加一个调度OLS类型的方法.

我正在使用的fit功能是,而不是创建一个新的通用函数,fit我将从StatsBase包中导入它并添加一个新方法来拟合我们的OLS类型.现在,当使用参数的权限类型调用时,我的fit方法和fit其他包中的任何其他方法都会被调度.我这样做的原因是因为如果我创建了一个新的fit函数,它将会影响StatsBase中的那个.对于在Julia中导出的函数,通常更好的是扩展和现有的规范泛型函数,而不是创建自己的函数,并冒险在基础或其他包中隐藏函数.

如果其他一些包导出了他们自己的describe泛型函数,并且在我们的OLSRegression包之后加载会使命令describe(m)失误.我们仍然可以describe使用完全限定的名称访问我们的函数,即OLSRegression.describe.

EDIT2:关于这些::Type{OLS}东西.

在函数调用fit(OLS, [1,2,5,4], [2,2,4,6]) OLS中没有括号,这意味着我没有构造OLS类型的实例并将其传递给函数,而是我将类型本身传递给方法.

obj::OLS::OLS部分中指定该对象应该是该类型的实例OLS.在obj之前这是我该实例结合为我们在函数体的名称.::Type{OLS}两种方式有所不同.它不是指定参数应该是类型的实例OLS,而是指定参数应该是Type参数化的实例OLS.冒号之前没有任何东西,因为我没有将它绑定到任何变量名,因为我不需要在函数体中使用它.

我这样做的原因只是为了帮助消除不同方法之间的歧义fit.其他一些软件包也可能fit在StatsBase中扩展该功能.如果我们都使用函数签名,就像StatsBase.fit(x, y)Julia不知道要分派哪个方法.相反,如果我使用类似的函数签名StatsBase.fit(::Type{OLS}, x, y)而其他包执行类似的操作StatsBase.fit(::Type{NLLS}, x, y),则方法消除歧义,并且用户可以将类型作为第一个参数传递以指定他想要的方法.