La solution de Tomas est assez élégante: elle est courte, purement fonctionnelle et paresseuse. Je pense que cela peut même être récursif. En outre, il produit des permutations lexicographiques. Cependant, nous pouvons améliorer les performances deux fois en utilisant une solution impérative en interne tout en exposant une interface fonctionnelle à l'extérieur.
La fonction permutations
prend une séquence générique e
ainsi qu'une fonction de comparaison générique f : ('a -> 'a -> int)
et donne paresseusement des permutations immutables lexicographiquement. La fonction de comparaison nous permet de générer des permutations d'éléments qui ne sont pas nécessairement comparable
ainsi que de spécifier facilement des ordres inversés ou personnalisés.
La fonction interne permute
est la mise en œuvre impérative de l'algorithme décrit here.La fonction de conversion let comparer f = { new System.Collections.Generic.IComparer<'a> with member self.Compare(x,y) = f x y }
nous permet d'utiliser la surcharge System.Array.Sort
qui effectue des tris personnalisés sur une sous-gamme à l'aide d'un IComparer
.
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' }
Maintenant, pour des raisons pratiques, nous avons les éléments suivants où let flip f x y = f y x
:
let permutationsAsc e = permutations compare e
let permutationsDesc e = permutations (flip compare) e
FYI - Set.mem a été renommé Set.contains –
@Stephen, j'ai édité le code pour convenir ... – Benjol