【问题标题】:Julia: What is the best way to set up a OOP model for a libraryJulia:为库设置 OOP 模型的最佳方法是什么
【发布时间】:2014-11-08 05:37:14
【问题描述】:

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

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

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

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

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

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

【问题讨论】:

  • 我想我不明白你在做什么。从function describe(m::Model) 看来您已经有了模型类型?最后一个函数是该类型的构造函数吗?
  • @spencerlyon2 这只是伪代码。让我重写一下,这样更清楚
  • @spencerlyon2 基本上我正在尝试找到一种方法来重写一个函数,所以我不必做像Model(1,2,4,5,"no","false"...etc) 这样的事情,所以可以有单独的命令来描述或在数据集上实现不同的函数.
  • 很自然的做法是为Model 定义一个type,然后为该类型编写各种方法(描述、实现函数f)。恐怕没有更多代码可以解决,我不能比这更具体。手册中关于typesmethods 的部分是相关的文档。
  • @spencerlyon2 是的,这是真的,但是什么元素最好放在类型中?我不太擅长这个,文档有点混乱。

标签: function oop julia


【解决方案1】:

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

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

module OLSRegression

end

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

type OLS
    a::Real
    b::Real
end

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

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

编辑:让我在方法、多重调度和阴影上添加一些 cmets。

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

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

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

Julia 的调度超越了单一调度到多个调度。在单次分派中,调用cat.run("fast")cat.run(5) 将分派到同一个方法,并且由方法决定使用不同类型的第二个参数执行不同的操作。在 Julia 中,run(cat, "fast")run(cat, 5) 分派到不同的方法。

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

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

如果其他一些包导出了他们自己的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) 之类的操作,则方法会消除歧义,并且用户可以将类型作为第一个参数传递以指定他想要的方法。

【讨论】:

  • 由于 julia 的设置方式,我猜 function describe 仅适用于 obj::OLS 所以如果我有另一个包中的函数描述,我会通过 OLSRegression.describe() 表明我正在使用这个函数
  • @ccsv 我添加了一些 cmets,希望可以清除它。
  • 不错的答案。快速提问:为什么.fit 函数需要输入::Type{OLS},而describe 函数需要输入obj::OLS?在后一种情况下,它就像调度的输入过滤器一样工作,但我无法弄清楚第一个的语法......OLS 并不像那里的输入那样工作。
  • @ccsv 我在答案中添加了更多内容。希望对您有所帮助。
【解决方案2】:

Matlab 倾向于鼓励使用单一功能,但在 Julia 中,将事物分解成更小的部分几乎总是更好。我不确定我是否真的理解你想要做的事情,但至于文档,请查看 DocileLexicon(成对使用)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-23
    • 2014-09-05
    • 1970-01-01
    • 2019-09-21
    • 2018-03-17
    • 1970-01-01
    相关资源
    最近更新 更多