Set
, ähnlich wie []
hat eine perfekt definierte monadische Operationen. Das Problem ist, dass sie erfordern, dass die Werte Ord
Constraint erfüllen, und daher ist es unmöglich, return
und ohne Einschränkungen zu definieren. Das gleiche Problem gilt für viele andere Datenstrukturen, die eine Art von Einschränkungen für mögliche Werte erfordern.Konstruieren von effizienten Monad-Instanzen auf `Set` (und anderen Containern mit Constraints) unter Verwendung der Fortsetzungs-Monade
Der Standardtrick (vorgeschlagen zu mir in haskell-cafe post) ist, Set
in die continuation monad zu wickeln. ContT
ist es egal, ob der zugrundeliegende Typ funktor irgendwelche Einschränkungen hat. Die Zwänge geworden nur benötigt, wenn Verpackung/Abwickeln Set
s in/aus Fortsetzungen:
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
Dieser Bedarf arbeitet. Zum Beispiel können wir eine nicht-deterministischen Funktion, der entweder erhöht dessen Argument von 1 oder lässt sie intakt simulieren:
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)
Tatsächlich stepN 5 0
fromList [0,1,2,3,4,5]
ergibt. Wenn wir []
Monade stattdessen verwendet, würden wir
[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]
stattdessen bekommen.
Das Problem ist Effizienz. Wenn wir stepN 20 0
aufrufen, dauert die Ausgabe ein paar Sekunden und stepN 30 0
endet nicht innerhalb einer angemessenen Zeit. Es stellt sich heraus, dass alle Set.union
Operationen am Ende ausgeführt werden, anstatt sie nach jeder monadischen Berechnung durchzuführen. Das Ergebnis ist, dass viele Set
s nur am Ende konstruiert und union
ed sind, was für die meisten Aufgaben nicht akzeptabel ist.
Gibt es einen Weg um diese Konstruktion effizient zu machen? Ich habe es versucht, aber ohne Erfolg.
(ich auch vermuten, dass es könnte einige Arten von theoretischen Grenzen liegen folgende von Curry-Howard Isomorphismus und Glivenko's theorem. Glivenko Theorem besagt, dass für jede Aussage Tautologie φ die Formel ¬¬φ kann in intuitionismus nachgewiesen werden Ich vermute jedoch, dass die Länge des Beweises (in normaler Form) exponentiell lang sein kann. Also könnte es Fälle geben, in denen eine Berechnung in die Fortsetzungsmonade exponentiell länger gemacht wird?)
Nun, so scheint es mir, dass es nicht eine wirklich effiziente 'Monad' Instanz für' Set' sein kann es sei denn, es gibt auch eine effiziente 'Functor'-Instanz. Und es fällt mir schwer zu sehen, wie Sie eine effiziente 'fmap' für' Set' erstellen können. [Die bestehende 'Karte' für' Set' ist n * log n.] (Http://hackage.haskell.org/packages/archive/containers/0.4.2.1/doc/html/Data-Set.html # g: 7) 'Set's implementiert als strenge Bäume, so Faulheit wird Ihnen auch nicht helfen. –
Ich denke, das Problem ist, dass die Monade nicht "weiß", dass Zahlen "Ord" oder sogar "Eq" haben. – PyRulez
@LuisCasillas Ein zusätzlicher _log n_ Faktor wäre in Ordnung, die Sache, die mich betrifft, ist die exponentielle Explosion. –