我正在尝试创建一个库。可以说我有一个模型,其中有一个输出,输入和描述函数的方程式。输入为:
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)
,它仍将分派给dog
或cat
类型的适当方法。这也是
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/