簡體   English   中英

Haskell可變地圖/樹

[英]Haskell mutable map/tree

我正在尋找Haskell中的可變(平衡)樹/ map / hash表,或者如何在函數內模擬它。 即,當我多次調用相同的函數時,結構將被保留。 到目前為止,我已經嘗試了Data.HashTable(可以,但有點慢)並嘗試了Data.Array.Judy,但我無法使其與GHC 6.10.4一起使用。 還有其他選擇嗎?

如果你想要可變狀態,你可以擁有它。 只是繼續傳遞更新的地圖,或將其保持在狀態monad(事實證明是相同的)。

import qualified Data.Map as Map
import Control.Monad.ST
import Data.STRef
memoize :: Ord k => (k -> ST s a) -> ST s (k -> ST s a)
memoize f = do
    mc <- newSTRef Map.empty
    return $ \k -> do
        c <- readSTRef mc
        case Map.lookup k c of
            Just a -> return a
            Nothing -> do a <- f k
                          writeSTRef mc (Map.insert k a c) >> return a

你可以像這樣使用它。 (實際上,您可能還想添加一種方法來清除緩存中的項目。)

import Control.Monad
main :: IO ()
main = do
    fib <- stToIO $ fixST $ \fib -> memoize $ \n ->
        if n < 2 then return n else liftM2 (+) (fib (n-1)) (fib (n-2))
    mapM_ (print <=< stToIO . fib) [1..10000]

您需要自擔風險不必安全地從線程狀態的需求中逃脫需要它的一切。

import System.IO.Unsafe
unsafeMemoize :: Ord k => (k -> a) -> k -> a
unsafeMemoize f = unsafePerformIO $ do
    f' <- stToIO $ memoize $ return . f
    return $ unsafePerformIO . stToIO . f'

fib :: Integer -> Integer
fib = unsafeMemoize $ \n -> if n < 2 then n else fib (n-1) + fib (n-2)

main :: IO ()
main = mapM_ (print . fib) [1..1000]

在@ Ramsey的答案的基礎上,我還建議你重新使用你的函數來獲取一個地圖並返回一個修改過的地圖。 然后使用良好的' Data.Map代碼,這在修改時非常有效。 這是一種模式:

import qualified Data.Map as Map

-- | takes input and a map, and returns a result and a modified map
myFunc :: a -> Map.Map k v -> (r, Map.Map k v)
myFunc a m = … -- put your function here

-- | run myFunc over a list of inputs, gathering the outputs
mapFuncWithMap :: [a] -> Map.Map k v -> ([r], Map.Map k v)
mapFuncWithMap as m0 = foldr step ([], m0) as
    where step a (rs, m) = let (r, m') = myFunc a m in (r:rs, m')
    -- this starts with an initial map, uses successive versions of the map
    -- on each iteration, and returns a tuple of the results, and the final map

-- | run myFunc over a list of inputs, gathering the outputs
mapFunc :: [a] -> [r]
mapFunc as = fst $ mapFuncWithMap as Map.empty
    -- same as above, but starts with an empty map, and ignores the final map

很容易抽象這個模式,並使mapFuncWithMap通用於以這種方式使用地圖的函數。

雖然你要求一個可變類型,但我建議你使用一個不可變的數據結構,並將連續版本作為參數傳遞給你的函數。

關於使用哪種數據結構,

問題是我不能使用(或者我不知道如何)使用非可變類型。

如果幸運的話,您可以將表數據結構作為額外參數傳遞給需要它的每個函數。 但是,如果您的表需要廣泛分發,您可能希望使用狀態monad ,其中state是表的內容。

如果你想要記憶,你可以嘗試Conal Elliott的博客中的一些懶惰的記憶技巧,但是一旦你超越整數論證,懶惰的記憶變得非常模糊 - 不是我建議你作為初學者嘗試的東西。 也許你可以發一個關於你想要解決的更廣泛問題的問題? 通常使用Haskell和可變性的問題是如何在某種范圍內包含突變或更新。

沒有任何全局可變變量,學習編程並不容易。

還有其他選擇嗎?

對像Data.Map這樣的純函數字典的可變引用。

如果我正確地閱讀了您的評論,那么您的結構總計可能需要約500k才能計算出來。 計算是昂貴的,因此您只需要執行一次,並且在后續訪問中,您只需要重新計算該值。

在這種情況下,使用Haskell的懶惰對您有利! ~500k不是那么大:只需構建一個所有答案的地圖,然后根據需要進行獲取。 第一次獲取將強制計算,相同答案的后續提取將重用相同的結果,如果您從未獲取特定計算 - 它永遠不會發生!

您可以使用3D點距離作為PointCloud.hs文件中的計算找到這個想法的小實現。 該文件使用Debug.Trace來記錄計算實際完成的時間:

> ghc --make PointCloud.hs 
[1 of 1] Compiling Main             ( PointCloud.hs, PointCloud.o )
Linking PointCloud ...

> ./PointCloud 
(1,2)
(<calc (1,2)>)
Just 1.0
(1,2)
Just 1.0
(1,5)
(<calc (1,5)>)
Just 1.0
(1,2)
Just 1.0

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM