Кэширование возможных значений функции, созданной во время выполнения - PullRequest
0 голосов
/ 23 марта 2012

У меня есть конструктор данных с несколькими конструкторами значений:

data DataType = C1 | C2 | C3 | ... | Cn

Я хотел бы построить функцию во время выполнения из этого типа данных в некоторые другие значения (фактически, я делаю это в монаде IO):

buildFun :: IO (DataType -> b)
buildFun = do
    ....
    return $ \x -> case x of
                       C1 -> someProcessesToGetTheValue C1
                       ...
                       Cn -> someProcessesToGetTheValue Cn

Будет ли это означать, что someProcessesToGetTheValue будет вызываться каждый раз, когда я вызываю возвращаемую функцию?

Я бы предпочел, чтобы Haskell оценил someProcessesToGetTheValue внутри buildFun (поскольку эти вызовы довольно дороги) и вернул функцию, которая возвращает эти полностью вычисленные выражения.

Могу ли я заставить это поведение? Возможно, сделав что-то вроде следующего?:

buildFun :: IO (DataType -> b)
buildFun = do
    C1value <- return $ someProcessesToGetTheValue C1
    ...
    Cnvalue <- return $ someProcessesToGetTheValue Cn
    return $ \x -> case x of
                       C1 -> C1value
                       ...
                       Cn -> Cnvalue

Ответы [ 2 ]

4 голосов
/ 23 марта 2012

Вам вообще не нужно включать монаду ввода-вывода (и действительно do { x <- return v; ... } идентична let x = v in ...), просто привязайте значения вне лямбды:

buildFun :: IO (DataType -> b)
buildFun = do
    let v1 = someProcessesToGetTheValue C1
    ...
    return $ \x -> case x of { C1 -> v1; ... }

Haskell не делаетдействительно укажите что-нибудь о поведении оценки во время выполнения, но во всех распространенных реализациях это гарантирует, что результаты shared ;см. Что означает «выплыл»? для получения дополнительной информации.

Однако, он все равно не будет оценивать v 1 v n внутри buildFun;вместо этого они будут оцениваться каждый раз, когда оценивается соответствующий результат возвращаемой вами функции.Если вы хотите принудительно оценить их заранее, вы можете сказать let !v1 = someProcessesToGetTheValue C1 (для этого требуется расширение BangPatterns) или v1 <- evaluate $ someProcessesToGetTheValue C1 (из Control.Exception; это будет лучше, если someProcessesToGetTheValue C1 может выдатьисключение).

0 голосов
/ 26 марта 2012

Вместо функции, почему бы не определить некоторую структуру данных, например список, всех результатов оценки этой функции (проиндексированных по позиции конструктора в типе данных)?Например, что-то вроде этого (не проверено):

data DataType = C1 | C2 | C3 | ... | Cn deriving (Enum, Bounded)

cachedValues :: [b]
cachedValues = map someProcessesToGetTheValue ([minBound .. maxBound] :: [DataType])

getCachedValue :: DataType -> b
getCachedValue x = cachedValues !! (fromEnum x)

Поскольку Haskell ленив, он будет хранить thunk до первого запуска, после чего запоминает значение.

(Если обход списка по размеру n неэффективен; вместо него можно использовать массив или Map. Идея та же.)

...