Tomas 'Lösung ist ziemlich elegant: Sie ist kurz, rein funktional und faul. Ich denke, es kann sogar Schwanz-rekursiv sein. Außerdem erzeugt es lexikographisch Permutationen. Wir können die Leistung jedoch zweifach verbessern, indem wir intern eine imperative Lösung verwenden und gleichzeitig eine funktionale Schnittstelle extern freilegen.
Die Funktion permutations
nimmt eine generische Sequenz e
sowie eine generische Vergleichsfunktion f : ('a -> 'a -> int)
und lazily ergibt unveränderbare Permutationen lexikografisch. Das Vergleichsfunktional ermöglicht es uns, Permutationen von Elementen zu generieren, die nicht notwendigerweise comparable
sind, und auch einfach umgekehrte oder benutzerdefinierte Ordnungen zu spezifizieren.
Die innere Funktion permute
ist die zwingende Implementierung des beschriebenen Algorithmus here.Die Umwandlungsfunktion let comparer f = { new System.Collections.Generic.IComparer<'a> with member self.Compare(x,y) = f x y }
ermöglicht es uns, die System.Array.Sort
Überladung zu verwenden, die unter Verwendung von IComparer
in-place benutzerdefinierte Sortierungen durchführt.
let permutations f e =
///Advances (mutating) perm to the next lexical permutation.
let permute (perm:'a[]) (f: 'a->'a->int) (comparer:System.Collections.Generic.IComparer<'a>) : bool =
try
//Find the longest "tail" that is ordered in decreasing order ((s+1)..perm.Length-1).
//will throw an index out of bounds exception if perm is the last permuation,
//but will not corrupt perm.
let rec find i =
if (f perm.[i] perm.[i-1]) >= 0 then i-1
else find (i-1)
let s = find (perm.Length-1)
let s' = perm.[s]
//Change the number just before the tail (s') to the smallest number bigger than it in the tail (perm.[t]).
let rec find i imin =
if i = perm.Length then imin
elif (f perm.[i] s') > 0 && (f perm.[i] perm.[imin]) < 0 then find (i+1) i
else find (i+1) imin
let t = find (s+1) (s+1)
perm.[s] <- perm.[t]
perm.[t] <- s'
//Sort the tail in increasing order.
System.Array.Sort(perm, s+1, perm.Length - s - 1, comparer)
true
with
| _ -> false
//permuation sequence expression
let c = f |> comparer
let freeze arr = arr |> Array.copy |> Seq.readonly
seq { let e' = Seq.toArray e
yield freeze e'
while permute e' f c do
yield freeze e' }
Jetzt Einfachheit halber haben wir folgendes wo let flip f x y = f y x
:
let permutationsAsc e = permutations compare e
let permutationsDesc e = permutations (flip compare) e
FYI - Set.mem wurde in Set.contains –
umbenannt @Stephen, ich habe den Code nachbearbeitet ... – Benjol