Mi última mejor respuesta
//mini-extension to List for removing 1 element from a list
module List =
let remove n lst = List.filter (fun x -> x <> n) lst
//Node type declared outside permutations function allows us to define a pruning filter
type Node<'a> =
| Branch of ('a * Node<'a> seq)
| Leaf of 'a
let permutations treefilter lst =
//Builds a tree representing all possible permutations
let rec nodeBuilder lst x = //x is the next element to use
match lst with //lst is all the remaining elements to be permuted
| [x] -> seq { yield Leaf(x) } //only x left in list -> we are at a leaf
| h -> //anything else left -> we are at a branch, recurse
let ilst = List.remove x lst //get new list without i, use this to build subnodes of branch
seq { yield Branch(x, Seq.map_concat (nodeBuilder ilst) ilst) }
//converts a tree to a list for each leafpath
let rec pathBuilder pth n = // pth is the accumulated path, n is the current node
match n with
| Leaf(i) -> seq { yield List.rev (i :: pth) } //path list is constructed from root to leaf, so have to reverse it
| Branch(i, nodes) -> Seq.map_concat (pathBuilder (i :: pth)) nodes
let nodes =
lst //using input list
|> Seq.map_concat (nodeBuilder lst) //build permutations tree
|> Seq.choose treefilter //prune tree if necessary
|> Seq.map_concat (pathBuilder []) //convert to seq of path lists
nodes
La función permutaciones funciona mediante la construcción de un n-ario (sin duda más corto!) árbol que representa todas las permutaciones posibles de la lista de "cosas" pasadas, luego atraviesa el árbol para construir una lista de listas. Usar 'Seq' mejora dramáticamente el rendimiento ya que hace que todo sea flojo.
El segundo parámetro de la función de permutaciones permite que el que llama defina un filtro para 'podar' el árbol antes de generar las rutas (ver mi ejemplo a continuación, donde no quiero ningún cero inicial).
Algunos ejemplo de uso: Nodo < 'a> es genérico, por lo que puede hacer permutaciones de 'nada':
let myfilter n = Some(n) //i.e., don't filter
permutations myfilter ['A';'B';'C';'D']
//in this case, I want to 'prune' leading zeros from my list before generating paths
let noLeadingZero n =
match n with
| Branch(0, _) -> None
| n -> Some(n)
//Curry myself an int-list permutations function with no leading zeros
let noLZperm = permutations noLeadingZero
noLZperm [0..9]
(Gracias especiales a Tomas Petricek, cualquier comentario de bienvenida)
FYI - Set.mem ha cambiado el nombre Set.contains –
@Stephen, he editado el código para adaptarlo ... – Benjol