看起來好像你正試圖在Julia上特定的OOP風格,這並不是一個好的選擇。朱莉婭沒有課。而是使用類型,在這些類型上分派的函數和封裝整個模塊的組合。
作爲一個編制的例子讓我們做一個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)
調度來分離方法。
我見過朱莉婭的創造者稱之爲動詞語言和傳統的OOP語言名詞語言。在名詞語言中,您將方法附加到對象(名詞),但在Julia中,您將方法附加到泛型函數(動詞)。在上面的模塊中,我同時創建了一個新的通用函數describe
(因爲沒有該名稱的通用函數)並在其上附加了一個在OLS
類型上分派的方法。
我與fit
功能做的是什麼,而不是創建一個名爲fit
我從StatsBase包導入並增加了對我們的擬合類型OLS
的新方法,新的通用功能。現在我的fit
方法和其他fit
在其他包中的方法被調用時與參數的權限類型調用。我這樣做的原因是因爲如果我創建了一個新的擬合函數,它會在StatsBase中隱藏這個函數。對於在Julia中導出的函數,通常更好的方法是擴展現有的規範泛型函數,而不是創建自己的風險函數並在基礎或其他包中創建函數。
如果其他軟件包導出了自己的describe
泛型函數,並且被加載到我們的OLSRegression
軟件包後面,那麼將會使命令describe(m)
發生錯誤。我們仍然可以使用完全限定名稱訪問我們的describe
函數,即OLSRegression.describe
。
編輯2:關於::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)
這樣的函數簽名,朱莉婭不知道分派哪個方法。相反,如果我用一個函數簽名像StatsBase.fit(::Type{OLS}, x, y)
和其他包裝不喜歡的東西StatsBase.fit(::Type{NLLS}, x, y)
,那麼方法消除歧義,並且用戶可以通過類型指定他希望該法的第一個參數。
我不認爲我明白你正在嘗試做的。從'函數describe(m :: Model)'看起來你已經有了一個模型類型?最後一個函數是該類型的構造函數嗎? – spencerlyon2 2014-11-08 18:17:13
@ spencerlyon2這只是僞代碼。讓我重寫它,所以它更清晰 – ccsv 2014-11-09 00:31:43
@ spencerlyon2基本上我試圖找到一種方法來重寫一個函數,所以我不需要做類似'Model(1,2,4,5,「no」,「false 「...等)」,因此可以有單獨的命令來描述數據集,或對數據集執行不同的功能。 – ccsv 2014-11-09 00:38:10