Я борюсь с общей проблемой о том, как сделать вычисление с учетом состояния в Haskell ленивым результатом. Например. следующий простой алгоритм может быть выражен с помощью генератора Python в виде расчетного состояния, но «ленивого» вычисления, выполняя только шаги, необходимые для достижения следующего оператора , а затем возвращает поток управления вызывающему абоненту до тех пор, пока не будет запрошен следующий элемент :Как сделать вычисление ST произвести ленивый поток результата (или действовать как совместная процедура)?
def solveLP(vmax0, elems):
elem_true_ixs = [ [ ei for ei, b in enumerate(row) if b ] for row in elems ]
return go(vmax0, elem_true_ixs)
def go(vmax, mms):
if not mms:
yield []
else:
for ei in mms[0]:
maxcnt = vmax[ei]
if not maxcnt > 0:
continue
vmax[ei] = maxcnt-1 # modify vmax vector in-place
for es in go(vmax, mms[1:]):
# note: inefficient vector-concat operation
# but not relevant for this question
yield [ei]+es
vmax[ei] = maxcnt # restore original vmax state
for sol in solveLP([1,2,3],[[True,True,False],[True,False,True]]):
print sol
# prints [0,2], [1,0], and [1,2]
Это может быть легко переведено на ленивое вычисление Haskell (например, когда m
специализируется на Logic
или []
), например,
import Control.Monad
import qualified Data.Vector.Unboxed as VU
solveLP :: MonadPlus m => VU.Vector Int -> [[Bool]] -> m [Int]
solveLP vmax0 elems = go vmax0 elemTrueIxs
where
-- could be fed to 'sequence'
elemTrueIxs = [ [ ei | (ei,True) <- zip [0::Int ..] row ] | row <- elems ]
go vmax [] = return []
go vmax (m:ms) = do
ei <- mlist m
let vmax' = vmax VU.// [(ei, maxcnt-1)] -- this operation is expensive
maxcnt = vmax VU.! ei
guard $ maxcnt > 0
es <- go vmax' ms
return $ (ei:es)
mlist = msum . map return
... но я хотел бы быть в состоянии приблизиться к первоначальной реализации в Python, с помощью изменяемых векторов, и изменения одного vmax0
вектора на месте (как мне просто нужно для увеличения/уменьшения один элемент и копирование всего вектора только для замены одного элемента - это довольно накладные расходы, чем дольше вектор становится); обратите внимание, что это всего лишь игрушечный пример для класса алгоритмов, которые я пытался реализовать.
Так что мой вопрос - если у вас есть способ это сделать - как я могу выразить такой алгоритм с учетом состояния в ST monad, все еще имея возможность возвращать результаты обратно вызывающему абоненту, как только они будут созданы во время вычисления? Я пробовал комбинировать монаду ST с монадом-монадом с монада-трансформаторами, но я не мог понять, как заставить его работать ...
Общее состояние в этом случае не может храниться в 'STRef', поэтому ответ не так прост, как этот. – dflemstr
@dflemstr Общее состояние * может *, однако, храниться в 'STArray', поэтому ответ действительно так же прост, как и этот. –
@ Даниэль Вагнер, да, используя 'STArray', это возможно; однако я не вижу этого ответа, касающегося причуд использования 'STArray's :) – dflemstr