Estoy luchando con el problema general sobre cómo hacer que un cálculo con estado en Haskell genere resultados de forma perezosa. P.ej. el siguiente algoritmo simple se puede expresar con la ayuda de la instalación del generador de Python como un cálculo con estado pero "flojo", realizando solo los pasos necesarios para llegar a la siguiente instrucción yield
y luego devolviendo el flujo de control al llamante hasta que se solicite el siguiente elemento :¿Cómo hacer que el cálculo ST produzca un flujo de resultados perezoso (u opere como una co-rutina)?
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]
Esto puede ser fácilmente traducido a un cálculo Haskell perezoso (por ejemplo, cuando m
está especializada para Logic
o []
), por ejemplo
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
... pero me gustaría ser capaz de acercarse a la implementación de Python original, mediante el uso de vectores mutables, y la modificación de un único vector de vmax0
in situ (como acabo necesito para aumentar/disminuir un solo elemento, y copiar todo el vector para reemplazar un solo elemento es bastante sobrecarga cuanto más largo se vuelve el vector); tenga en cuenta que esto es solo un ejemplo de juguete para una clase de algoritmos que he estado tratando de implementar
Así que mi pregunta es - suponiendo que haya una manera de lograr eso - ¿cómo puedo expresar un algoritmo con tanto estado en la mónada de ST mientras se pueden devolver los resultados a la persona que llama tan pronto como se producen durante el cálculo? Intenté combinar la mónada ST con una mónada de lista mediante mónada-transformadores, pero no pude encontrar la manera de hacerlo funcionar ...
El estado compartido en este caso no se puede almacenar en un 'STRef', por lo que la respuesta no es tan simple como esto. – dflemstr
@dflemstr El estado compartido * puede *, sin embargo, ser almacenado en un 'STArray', por lo que la respuesta es tan simple como esto. –
@DanielWagner, sí, usando 'STArray's, es posible; sin embargo, no veo esta respuesta que se ocupe de las peculiaridades de usar 'STArray's :) – dflemstr