Es gibt zwei Probleme mit dieser Funktion. Erstens rekuriert es zweimal, was es exponentiell ineffizienter als notwendig macht (wenn wir die exponentielle Anzahl der Ergebnisse ignorieren ...), weil jeder Teilbaum jedes Mal für alle überlappenden Teilmengen neu berechnet wird; dies kann durch let
ing der rekursive Aufruf denselben Wert festgelegt werden: bereits Dieses
subsets' :: [a] -> [[a]]
subsets' [] = [[]]
subsets' (x:xs) = let s = subsets' xs
in map (x:) s ++ s
erlauben Sie length $ subsets' [1..25]
in wenigen Sekunden zu berechnen, während length $ subsets [1..25]
nimmt ... na ja, ich habe nicht warten;)
Das andere Problem ist, dass mit Ihrer Version, wenn Sie es eine unendliche Liste geben, wird es auf die unendlichen Schwanz dieser Liste zuerst rekursieren. Um alle endlichen Teilmengen auf sinnvolle Weise zu erzeugen, müssen wir zwei Dinge sicherstellen: erstens müssen wir jede Menge aus kleineren Mengen aufbauen (um die Terminierung zu gewährleisten), und zweitens sollten wir sicherstellen, dass die Reihenfolge 0.ist (dh nicht erzeuge die Liste [[1], [2], ...]
zuerst und komme nie zum Rest). Dazu gehen wir von [[]]
und rekursiv das aktuelle Element, um alles fügen wir bereits erstellt haben, und dann die neue Liste für den nächsten Schritt denken Sie daran:
subsets'' :: [a] -> [[a]]
subsets'' l = [[]] ++ subs [[]] l
where subs previous (x:xs) = let next = map (x:) previous
in next ++ subs (previous ++ next) xs
subs _ [] = []
die in dieser Reihenfolge ergibt:
*Main> take 100 $ subsets'' [1..]
[[],[1],[2],[2,1],[3],[3,1],[3,2],[3,2,1],[4],[4,1],[4,2],[4,2,1],[4,3],[4,3,1],[4,3,2],[4,3,2,1],[5],[5,1],[5,2],[5,2,1],[5,3],[5,3,1],[5,3,2],[5,3,2,1],[5,4],[5,4,1],[5,4,2],[5,4,2,1],[5,4,3],[5,4,3,1],[5,4,3,2],[5,4,3,2,1],[6],[6,1],[6,2],[6,2,1],[6,3],[6,3,1],[6,3,2],[6,3,2,1],[6,4],[6,4,1],[6,4,2],[6,4,2,1],[6,4,3],[6,4,3,1],[6,4,3,2],[6,4,3,2,1],[6,5],[6,5,1],[6,5,2],[6,5,2,1],[6,5,3],[6,5,3,1],[6,5,3,2],[6,5,3,2,1],[6,5,4],[6,5,4,1],[6,5,4,2],[6,5,4,2,1],[6,5,4,3],[6,5,4,3,1],[6,5,4,3,2],[6,5,4,3,2,1],[7],[7,1],[7,2],[7,2,1],[7,3],[7,3,1],[7,3,2],[7,3,2,1],[7,4],[7,4,1],[7,4,2],[7,4,2,1],[7,4,3],[7,4,3,1],[7,4,3,2],[7,4,3,2,1],[7,5],[7,5,1],[7,5,2],[7,5,2,1],[7,5,3],[7,5,3,1],[7,5,3,2],[7,5,3,2,1],[7,5,4],[7,5,4,1],[7,5,4,2],[7,5,4,2,1],[7,5,4,3],[7,5,4,3,1],[7,5,4,3,2],[7,5,4,3,2,1],[7,6],[7,6,1],[7,6,2],[7,6,2,1]]
Vielen Dank! Es ist eine Lösung mit Monaden. Was ist mit ihnen ohne? – Gilgamesz
@Gilgamesz stellt sich heraus, dass die Monade nicht einmal benötigt wird. Die vorherige Version war sowieso falsch - jetzt funktioniert es mit einfachen Listen und hoffentlich richtig. – phg
@Gilgamesz Faulheit ist eines der Kernthemen von Haskell.Zum Beispiel ist der erste Punkt von phg ein Ergebnis von Haskells spezifischer Implementierung von Faulheit, die eine Graphenstruktur verwendet, um laufende Bewertungen darzustellen: IIRC, in einigen begrenzten Fällen wird der Compiler * die Duplizierung einfangen und die gleiche Datenstruktur teilen beide Instanzen --- aber nicht immer. Dies ist einer der Preise, die wir für Faulheit zahlen. Es ist eine Einschränkung des Compilers, auf die wir achten müssen, wenn wir uns auf die Leistung konzentrieren oder mit der Unendlichkeit umgehen. – jpaugh