Set
, de manière similaire à []
a des opérations monadiques parfaitement définies. Le problème est qu'ils nécessitent que les valeurs satisfassent Ord
contrainte, et il est donc impossible de définir return
et >>=
sans aucune contrainte. Le même problème s'applique à de nombreuses autres structures de données qui nécessitent un certain type de contraintes sur les valeurs possibles.Construire des instances de monad efficaces sur `Set` (et d'autres conteneurs avec des contraintes) en utilisant la monade de continuation
L'astuce standard (suggéré dans un haskell-cafe post) est d'envelopper Set
dans la monade de continuation. ContT
ne se soucie pas si le foncteur de type sous-jacent a des contraintes. Les contraintes ne deviennent nécessaires lorsque l'emballage/dépliage Set
s dans/de continuations:
import Control.Monad.Cont
import Data.Foldable (foldrM)
import Data.Set
setReturn :: a -> Set a
setReturn = singleton
setBind :: (Ord b) => Set a -> (a -> Set b) -> Set b
setBind set f = foldl' (\s -> union s . f) empty set
type SetM r a = ContT r Set a
fromSet :: (Ord r) => Set a -> SetM r a
fromSet = ContT . setBind
toSet :: SetM r r -> Set r
toSet c = runContT c setReturn
Cela fonctionne au besoin. Par exemple, nous pouvons simuler une fonction non-déterministe qui soit augmente son argument par 1 ou laisse intact:
step :: (Ord r) => Int -> SetM r Int
step i = fromSet $ fromList [i, i + 1]
-- repeated application of step:
stepN :: Int -> Int -> Set Int
stepN times start = toSet $ foldrM ($) start (replicate times step)
En effet, stepN 5 0
cède fromList [0,1,2,3,4,5]
. Si nous avons utilisé []
monade à la place, nous aurions
[0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5]
à la place.
Le problème est l'efficacité . Si nous appelons stepN 20 0
la sortie prend quelques secondes et stepN 30 0
ne se termine pas dans un délai raisonnable. Il s'avère que toutes les opérations Set.union
sont effectuées à la fin, au lieu de les effectuer après chaque calcul monadique. Le résultat est que Set
s sont exponentiellement construits et union
seulement à la fin, ce qui est inacceptable pour la plupart des tâches.
Y a-t-il un moyen de contourner le problème pour rendre cette construction efficace? J'ai essayé mais sans succès.
(je soupçonne même qu'il pourrait y avoir certains types de limites théoriques suivantes de isomorphisme de Curry-Howard et Glivenko's theorem. Théorème de Glivenko dit que pour toute tautologie propositionnelle φ la formule ¬¬φ peut être prouvée dans la logique intuitionniste Cependant, je soupçonne que la longueur de la preuve (sous sa forme normale) peut être exponentiellement longue, alors, peut-être, il pourrait y avoir des cas où enrouler un calcul dans la continuation monad le rendra exponentiellement plus long?)
Eh bien, il me semble qu'il ne peut pas être un exemple 'Monad' vraiment efficace pour' set' à moins qu'il n'y ait aussi une instance 'Functor' efficace. Et j'ai du mal à voir comment vous pouvez avoir un 'fmap' efficace pour' Set'. [La 'map' existante pour' Set' est n * log n.] (Http://hackage.haskell.org/packages/archive/containers/0.4.2.1/doc/html/Data-Set.html # g: 7) 'Set' est implémenté comme des arbres stricts, donc la paresse ne t'aidera jamais non plus. –
Je pense que le problème est que la monade ne "sait" pas que les nombres ont 'Ord' ou même' Eq'. – PyRulez
@LuisCasillas Un facteur additionnel de _log n_ serait OK, la chose qui me concerne est le gonflement exponentiel. –