Julia:为库设置 OOP 模型的最佳方法是什么 [英] Julia: What is the best way to set up a OOP model for a library

查看:20
本文介绍了Julia:为库设置 OOP 模型的最佳方法是什么的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to create a library. Lets say I have a model where I have a equation that outputs, input, and a describe function. The inputs would be:

x= [1,2,3,4,5,6]
y= [5,2,4,8,9,2]

我把它放到一个函数中:

And I put it into a function:

#=returns y values=#
function fit (x,a,b)
    y=ax+b
end

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

And another that outputs a summary using a describe function:

#=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

在 Julia 中最好的方法是什么?我应该使用 type 吗?

What is the best way of doing this in Julia? Should I use type?

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

Currently I am using a very large function such as:

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

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

But this is not very efficient or user friendly.

推荐答案

您似乎正试图将一种特定的 OOP 样式硬塞到 Julia 上,但实际上并不适合.Julia 没有课程.相反,您使用类型、在这些类型上分派的函数以及封装整体的模块的组合.

It seems like you are trying to shoehorn a specific OOP style onto Julia that is not really a good fit. Julia does not have classes. Instead you use a combination of types, functions that dispatch on those types, and modules that encapsulate the whole.

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

As a made up example lets make a package that does OLS regression. In order to encapsulate the code you wrap it in a module. Lets call it OLSRegression:

module OLSRegression

end

我们需要一个模型来存储回归结果并进行调度:

We need a model to store the results of the regression and to dispatch on:

type OLS
    a::Real
    b::Real
end

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

We then need a function to fit our OLS to data. Rather than creating out own fit function we can extend the one available in StatsBase.jl:

using StatsBase

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

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

Then we can create a describe function to print out the fitted model:

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

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

Lastly, we need to export the created types and functions from the module:

export OLS, describe, fit

整个模块放在一起是:

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

然后你会像这样使用它:

You would then use it like this:

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

让我添加一些关于方法、多重调度和阴影的评论.

Let me add some comments on methods, multiple dispatch and shadowing.

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

In a traditional OOP language you can have different objects that have methods with the same name. For example: we have the object dog and the object cat. They both have a method called run. I can call the appropriate run method with the dot syntax: dog.run() or cat.run(). This is single dispatch. The appropriate method is called based on the type of the first argument. Because of the importance of the first argument it appears before the method name instead of inside the parentheses.

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

In Julia this sort of dot syntax for calling methods, but it still has dispatch. Instead the first argument appears inside the parentheses just like all the other arguments. So you would do run(dog) or run(cat) and it still dispatches to the appropriate method for the dog or cat type.

describe(obj::OLS) 也是如此.我正在创建一个新方法描述并指定当第一个参数为 OLS 类型时应调用此方法.

This is also what is happening with describe(obj::OLS). I'm creating a new method describe and specifying that this method should be called when the first parameter is of type OLS.

Julia 的调度超越了单一调度到多个调度.在单次调度中,调用 cat.run("fast")cat.run(5) 将调度到相同的方法,而不同的方法取决于方法具有不同类型的第二个参数的事物.在 Julia 中,run(cat, "fast")run(cat, 5) 分派到不同的方法.

Julia's dispatch goes beyond single dispatch to multiple dispatch. In single dispatch the calls cat.run("fast") and cat.run(5) would dispatch to the same method and it is up to the method to do different things with the different types of the second parameter. In Julia run(cat, "fast") and run(cat, 5) dispatch to separate methods.

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

I've seen the creators of Julia call it a verb language and traditional OOP languages noun languages. In noun languages you attach methods to objects (nouns), but in Julia you attach methods to generic functions (verbs). In the module above I'm both creating a new generic function describe (because there is no generic function of that name) and attaching a method on it that dispatches on OLS types.

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

What I am doing with the fit function is that rather than creating a new generic function called fit I am importing it from the StatsBase package and adding a new method for fitting our OLS type. Now both my fit method and any other fit methods in other packages get dispatched to when called with the rights types of arguments. The reason I am doing this is because if I created a new fit function it would shadow the one in StatsBase. For functions you export in Julia it is generally better to extend and existing canonical generic function rather than create your own and risk shadowing a function in base or some other package.

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

If some other package exported their own describe generic function and was loaded after our OLSRegression package that would make the command describe(m) would error. We could still access our describe function with a fully qualified name, ie OLSRegression.describe.

关于 ::Type{OLS} 的东西.

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

In the function call fit(OLS, [1,2,5,4], [2,2,4,6]) OLS is called without parenthesis, which means I'm not constructing an instance of the OLS type and passing that to the function, but instead I'm passing the type itself to the method.

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

In obj::OLS the ::OLS part specifies that the object should be an instance of the type OLS. The obj before that is the name I am binding that instance to for us in the function body. ::Type{OLS} is different in two ways. Rather than specifying that the argument should be a instance of the type OLS, it specifies that the argument should be a instance of Type, parametrized with OLS. There is nothing before the colons because I am not binding it to any variable name, because I don't need to use it in the function body.

我这样做的原因只是为了帮助消除 fit 的不同方法之间的歧义.其他一些包也可能会扩展 StatsBase 中的 fit 函数.如果我们都使用像 StatsBase.fit(x, y) 这样的函数签名,Julia 将不知道分派到哪个方法.相反,如果我使用像 StatsBase.fit(::Type{OLS}, x, y) 这样的函数签名,而另一个包做了像 StatsBase.fit(::Type{NLLS}, x, y),然后方法消歧,用户可以通过类型作为第一个参数来指定他想要的方法.

The reason I am doing this is simply to help disambiguate between different methods of fit. Some other package might also be extending the fit function in StatsBase. If we both use a function signature like StatsBase.fit(x, y) Julia wouldn't know which method to dispatch to. Instead if I use a function signature like StatsBase.fit(::Type{OLS}, x, y) and the other package did something like StatsBase.fit(::Type{NLLS}, x, y), then the methods disambiguate, and the user can pass the type as the first parameter to specify which method he wants.

这篇关于Julia:为库设置 OOP 模型的最佳方法是什么的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆