Как заставить вычисление ST производить ленивый поток результатов (или работать как сопрограмма)?

Я борюсь с общей проблемой, как сделать вычисления с отслеживанием состояния в Haskell лениво генерирующими результаты. Например. следующий простой алгоритм может быть выражен с помощью средства генератора Python как вычисление с сохранением состояния, но «ленивое» вычисление, выполняющее только шаги, необходимые для достижения следующего оператора yield, а затем возвращающего поток управления вызывающей стороне. пока не будет запрошен следующий элемент:

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, которая по-прежнему может возвращать результаты вызывающей стороне, как только они будут получены во время вычисления? Я пытался объединить монаду ST с монадой списка через монады-преобразователи, но я не мог понять, как заставить это работать...

7
задан dflemstr 26 May 2012 в 20:03
поделиться