function - Julia :为图书馆建立OOP模型的最佳方法是什么

标签 function oop julia

我正在尝试创建一个库。可以说我有一个模型,其中有一个输出,输入和描述函数的方程式。输入为:

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


然后将其放入一个函数中:

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


另一个使用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


在朱莉娅中做到这一点的最佳方法是什么?我应该使用type吗?

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

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


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

最佳答案

似乎您正在尝试将特定的OOP样式放到Julia上,但这并不是一个很好的选择。朱莉娅没有课程。相反,您可以使用类型的组合,在这些类型上分派的函数以及封装整体的模块。

作为一个完整的示例,让我们制作一个进行OLS回归的程序包。为了封装代码,请将其包装在模块中。让我们称之为OLSRegression

module OLSRegression

end


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

type OLS
    a::Real
    b::Real
end


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

using StatsBase

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


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

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


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

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


然后,您可以像这样使用它:

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


编辑:让我在方法,多个调度和阴影上添加一些注释。

在传统的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)将分派给同一方法,该方法取决于使用不同类型的第二个参数来执行不同的事情。在Julia中,将run(cat, "fast")run(cat, 5)调度到单独的方法。

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

我使用fit函数所做的是,不是从StatsBase包中导入它,而是添加一个新的方法来适合我们的fit类型,而不是创建一个称为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的不同方法之间的歧义。其他一些软件包也可能会扩展StatsBase中的fit函数。如果我们都使用像StatsBase.fit(x, y)这样的函数签名,Julia将不知道要分派给哪个方法。相反,如果我使用像StatsBase.fit(::Type{OLS}, x, y)这样的函数签名,而其他程序包做了类似StatsBase.fit(::Type{NLLS}, x, y)的事情,那么这些方法将变得模棱两可,并且用户可以将类型作为第一个参数传递来指定他想要的方法。

关于function - Julia :为图书馆建立OOP模型的最佳方法是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26814150/

相关文章:

javascript - jquery - 未定义函数 - 从内联脚本调用此函数

R:for循环中的文本进度条

function - lua不修改函数参数

java - java中如何实现接口(interface)方法?

oop - 如何检查一个对象是否是 Haxe 中给定类的后代?

python - 寻求目录树数据表示的优雅设计

arrays - 在 Julia 的列中组合和堆叠数组

julia - 对角化稀疏酉矩阵

Javascript - 原型(prototype)设计的值(value)是什么?

julia - 使用 z 分数计算百分位数/概率