tôi bắt đầu làm việc trên một dự án xác định một automaton di động như một chức năng chuyển tiếp địa phương:Memoizing một chức năng effectful
newtype Cellular g a = Cellular { delta :: (g -> a) -> a }
Bất cứ khi nào g
là một Monoid
, người ta có thể xác định một sự chuyển tiếp toàn cầu bằng cách chuyển trọng tâm trước khi áp dụng sự chuyển đổi cục bộ. Điều này cho phép chúng tôi sau step
chức năng:
step :: Monoid g => Cellular g a -> (g -> a) -> (g -> a)
step cell init g = delta cell $ init . (g <>)
Bây giờ, chúng tôi chỉ đơn giản là có thể chạy các automaton bằng cách sử dụng iterate
. Và chúng ta có thể tiết kiệm rất nhiều (và tôi có nghĩa là rất nhiều: nó theo nghĩa đen tiết kiệm giờ) tái tính toán bởi memo
izing mỗi một trong những bước sau:
run :: (Monoid g, Memoizable g) => Cellular g a -> (g -> a) -> [g -> a]
run cell = iterate (memo . step cell)
Vấn đề của tôi là tôi khái quát hóa Cellular
-CelluarT
để tôi sẽ có thể sử dụng tác dụng phụ trong các quy tắc địa phương (ví dụ như sao chép một người hàng xóm ngẫu nhiên):
newtype CellularT m g a = Cellular { delta :: (g -> m a) -> m a }
Tuy nhiên, tôi chỉ muốn các hiệu ứng để được chạy lần do đó nếu bạn hỏi một tế bào nhiều lần những gì giá trị của nó là, các câu trả lời đều nhất quán. memo
không thành công ở đây vì nó tiết kiệm tính toán hiệu quả thay vì kết quả của nó.
Tôi không mong đợi điều này có thể đạt được mà không sử dụng các tính năng không an toàn. Tôi đã cố gắng để có một đi vào nó bằng cách sử unsafePerformIO
, một IORef
và Map g a
để lưu trữ các giá trị đã tính toán:
memoM :: (Ord k, Monad m) => (k -> m v) -> (k -> m v)
memoM =
let ref = unsafePerformIO (newIORef empty) in
ref `seq` loopM ref
loopM :: (Monad m, Ord k) => IORef (Map k v) -> (k -> m v) -> (k -> m v)
loopM ref f k =
let m = unsafePerformIO (readIORef ref) in
case Map.lookup k m of
Just v -> return v
Nothing -> do
v <- f k
let upd = unsafePerformIO (writeIORef ref $ insert k v m)
upd `seq` return v
Nhưng nó hoạt động theo những cách không thể đoán trước: memoM putStrLn
được memoized một cách chính xác trong khi memoM (\ str -> getLine)
giữ dòng lấy bất chấp sự cùng một đối số được truyền cho nó.
Bạn đang sử dụng thư viện ghi nhớ nào? [ghi nhớ] (https://hackage.haskell.org/package/memoize)? – Cirdec
Các kiểu dữ liệu của bạn tương đương với ['Cont' và' ContT'] (https://hackage.haskell.org/package/transformers/docs/Control-Monad-Trans-Cont.html). 'type Cellular ga = Cont ag' và' gõ CellularT mga = ContT amg' – Cirdec