Julia: What is the best way to set up an OOP model for a library

I am trying to create a library. Let's say I have a model where I have an equation that outputs, inputs and describes a function. The inputs will be:

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

      

And I included it in the function:

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

      

And another one that outputs a summary using the description 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

      

What's the best way to do this in Julia? Should I use type

?

I am currently using a very large function like:

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 convenient.

+3


source to share


2 answers


It looks like you are trying to train Julia's specific OOP style, which is not very suitable. Julia has no classes. Instead, you use a combination of types, functions dispatched by those types, and modules that encapsulate an integer.

As the example made, allows you to make a package that regresses OLS. To encapsulate your code, you wrap it in a module. Let's call it OLSRegression

:

module OLSRegression

end

      

We need a model to store the regression results and send:

type OLS
    a::Real
    b::Real
end

      

Then we need a function to match our OLS data. Instead of creating our own function, fit

we can extend it available in StatsBase.jl:

using StatsBase

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

      

We can then create a function describe

to print the installed model:

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

      

Finally, we need to export the generated types and functions from the module:

export OLS, describe, fit

      

All combined module:

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

      

Then you will 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

      

The EDIT: . Let me add some comments about methods, multiple dispatch and shadow copying.

In traditional OOP language, you can have different objects that have methods with the same name. For example: we have an object dog

and an object cat

. They both have a method called run

. I can call the corresponding method run

with the dot: dog.run()

or syntax cat.run()

. This is a one-time dispatch. The corresponding 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 rather than inside parentheses.

Julia has this type of dot syntax for calling methods, but it still has dispatch. Instead, the first argument appears inside parentheses, like all other arguments. So you would do run(dog)

or run(cat)

, and it still dispatches the appropriate method for the type dog

or cat

.

This also happens with describe(obj::OLS)

. I am creating a new method that describes and indicates that this method should be called when the first parameter is of type OLS

.

Sending one mail outside of a single dispatch throughout Japan will send out. On one dispatch, calls cat.run("fast")

and cat.run(5)

will send the same method, and it is this method that should perform different actions with different types of the second parameter. At Julia run(cat, "fast")

and run(cat, 5)

individual techniques go.

I've seen the creators of Julia refer to it as the verb language and traditional OOP languages. In noun languages, you attach methods to objects (nouns), but in Julia, you attach methods to generic functions (verbs). In the above module, I create a new generic function describe

(because there is no generic function of that name) and attach a method to it that dispatches by types OLS

.

What I do with a function fit

is, instead of creating a new generic function called, fit

I import it from the StatsBase package and add a new method to set our type OLS

. Now both my method fit

and any other methods fit

in other packages are dispatched when called with the type rights of the arguments. The reason I am doing this is because if I were to create a new fit function it would shade it in StatsBase. For functions that you export to Julia, it is usually best to extend and use an existing canonical generic function rather than creating your own and risk shading the function in the base or other package.

If any other package exported its own describe

common function and was loaded after our package OLSRegression

that would make the command an describe(m)

error. We could access our describe

fully qualified function , i.e. OLSRegression.describe

...

EDIT2: Regarding the stuff ::Type{OLS}

.

In a function call, it fit(OLS, [1,2,5,4], [2,2,4,6])

OLS

is called without parentheses, which means I don't instantiate the type OLS

and pass it to the function, but instead I pass the type itself to the method.

The obj::OLS

part ::OLS

indicates that the object must be an instance of the type OLS

. obj

before that name, I bind this instance for us in the body of the function. ::Type{OLS}

differs in two ways. Instead of specifying that the argument should be an instance of the type OLS

, it specifies that the argument should be an instance Type

parameterized with OLS

. There is nothing before the colons because I don't bind it to any variable name because I don't need to use it in the function body.

The reason I am doing this is to help disambiguate between the various methods fit

. Several other packages may also extend the function fit

in StatsBase. If we both use a type function signature StatsBase.fit(x, y)

, Julie doesn't know which method to send to. Instead, if I use a function signature, for example StatsBase.fit(::Type{OLS}, x, y)

, and another package did something like StatsBase.fit(::Type{NLLS}, x, y)

, then the methods can be ambiguous and the user can pass that type as the first parameter to indicate which method he wants.

+8


source


Matlab strives to encourage one monolithic function, but in Julia it is almost always better to break things down into smaller chunks. I'm not sure I really understand what you are trying to do, but as far as documentation goes, have a look at Docile and Lexicon (which work as a couple).



+1


source







All Articles