2012-04-06 51 views
7

有沒有什麼辦法可以強制GHC在特定值的生命週期內對特定計算進行thunk操作?Haskell中是否存在任何隱式存儲?

我很明顯可以將值放入記錄中,爲所述計算的結果創建惰性記錄條目,並創建一個製造函數,該函數構建記錄並將值轉換爲所述條目。

我討厭需要從記錄中拉出原始值,但每次我都需要它。 Haskell並沒有像C++或Java這樣的關係。

是否有任何技巧用於記憶具有相同參數的函數的多個不相關調用的值?

我可以隱約想象各種形式的依賴類型的技巧會有效地告訴編譯器多種用法即將到來。 Haskell中沒有任何依賴類型,但可能是隱含參數的東西?我想不是,但我想我會問。也許是一個編譯指示?


想象我已經針對我需要得到的有理數的矢量,存儲爲公分母和numerators的矢量Necklace數據結構的載體。

{-# LANGUAGE ImplicitParams #-} 
import qualified Data.Vector as V 

data Necklace = Necklace { ... } 
necklace_length n = ... 

denominator :: (necklaces :: V.Vector Necklace) => Int 
denominator = V.foldl' lcm 30 $ V.map necklace_length ?necklaces 

numerators :: (necklaces :: V.Vector Necklace) => V.Vector Int 
numerators = V.map f ?necklaces 
    where f x = ... denominator ... 

kittytoy :: (necklaces :: V.Vector Necklace) => Meow -> ... 
kittytoy = \meow -> ... numerators ... 

先驗,我期望的是,如果我調用kittytoy數百萬次,每次用不同的參數meow,然後GHC產生調用numerators一百萬次,每次用相同的隱式參數necklaces代碼。

然而很明顯numerators只需要調用一次,但第一次被分配的時候?necklaces,這意味着GHC可能會注意到這種優化。

甚至應該有一個明確的代碼重構方法,使用模板haskell通過生成代碼?numerators = numerators以及添加numerators :: V.Vector Int來鍵入調用它的函數的約束來顯式傳遞thunk。

+0

'kittytoy necklaces = let n = numerators項鍊在\ meow - > stuff'絕對應該記憶'n'。不知道它是否也會如此。 – yairchu 2012-04-06 21:24:21

+0

你的意思是'讓k = kittytoy在''用'k'替換對'kittytoy'的每個調用?是的,我同意應該記憶隱式參數'?necklaces',但是我真的需要這麼做嗎? – 2012-04-06 21:27:00

+0

啊,不,我試圖在'* kittytoy'的調用之外記錄'numerators' *,因爲'kittytoy'本身被調用了幾百萬次。是的,這是一個有效的觀點,'kittytoy'應該是一個lambda表達式,無論如何,爲了清晰起見,我會對其進行編輯。 – 2012-04-06 21:29:24

回答

7

您可能正在尋找純粹的memoisation,執行data-memocombinators。基本上,它通過創建一個(懶惰的,可能無限的)樹結構和每個葉子上函數的所有可能值,然後創建一個新函數來簡單地訪問相關位置的值。比如,你可以寫函數Bool -> a這樣的memoiser:

memoBool :: (Bool -> a) -> (Bool -> a) 
memoBool f = 
    let fTrue = f True 
     fFalse = f False 
    in \b -> if b then fTrue else fFalse 

在這種情況下,「樹結構」是相當盆景,只有兩片葉子。

數據memocombinators包這件事在Memo a類型,如pair :: Memo a -> Memo b -> Memo (a, b)定義爲forall r. (a -> r) -> (a -> r),有用的組合子(讀:如果你能memoise參數類型a的功能和參數類型b的memoise功能,可以memoise的功能參數類型(a, b))。

這是純粹的,非常優雅,依靠基本上所有Haskell實現實現共享(這是使您的記錄想法工作的同一件事)。不幸的是,它的速度並不是很快,所以在實際應用中,您可能想要使用uglymemo,而在幕後使用可變的Map(但暴露了外部純粹的接口)。

+0

我認爲不是,雖然知道存在這很好。當隱式參數'?necklaces'發生變化時,我想要將memoisation轉儲。我發現yairchu評論澄清,基本上隱含的參數可能會創造額外的機會,但只能通過明確地製作lambda表達式。感覺應該有更多可能,但沒關係。 – 2012-04-06 21:49:00

+0

那麼,你只需寫出'分子:: V.Vector項鍊 - > V.Vector Int'。然後,具有相同參數的調用將產生相同的結果,而無需重新計算。無可否認,根據「Vector」的大小,以樹狀結構查看它的成本可能會超過從頭開始執行「分子」計算的成本。 GHC有必要的設施來建立一個基於輸入值的指針標識的memoisation庫,但我不知道Hackage是否有任何關於它的內容。 – ehird 2012-04-06 21:53:03

+0

順便說一下,隱式參數是一個非常罕見的擴展,大多數人避免它們 - 我只看到兩段實際使用它們的代碼,並且它們有令人討厭的陷阱,比如向定義中添加類型簽名以更改其值。 – ehird 2012-04-06 21:53:09

0

Philip JFanswer here所述,還存在另一種合理的方法,因爲type現在定義了類型約束的同義詞。

你也許可以用於創建所有的各種衍生值隱含參數的類型約束創建一個同義詞:

type Necklaces = (necklaces :: V.Vector Necklace, 
        denominator :: Int, 
        numerators :: V.Vector Int) 

kittytoy :: (Necklaces) => Meow -> ... 

你會使用模板哈斯克爾建設某種形式的初始分配中的所有值一樣?numerators 。我會和它一起玩,看看它是否有效。

相關問題