Вычисление перестановок в F#

В Java все переменные, которые вы объявляете, на самом деле являются «ссылками» на объекты (или примитивы), а не самими объектами.

При попытке выполнить один метод объекта , ссылка просит живой объект выполнить этот метод. Но если ссылка ссылается на NULL (ничего, нуль, void, nada), то нет способа, которым метод будет выполнен. Тогда runtime сообщит вам об этом, выбросив исключение NullPointerException.

Ваша ссылка «указывает» на нуль, таким образом, «Null -> Pointer».

Объект живет в памяти виртуальной машины пространство и единственный способ доступа к нему - использовать ссылки this. Возьмем этот пример:

public class Some {
    private int id;
    public int getId(){
        return this.id;
    }
    public setId( int newId ) {
        this.id = newId;
    }
}

И в другом месте вашего кода:

Some reference = new Some();    // Point to a new object of type Some()
Some otherReference = null;     // Initiallly this points to NULL

reference.setId( 1 );           // Execute setId method, now private var id is 1

System.out.println( reference.getId() ); // Prints 1 to the console

otherReference = reference      // Now they both point to the only object.

reference = null;               // "reference" now point to null.

// But "otherReference" still point to the "real" object so this print 1 too...
System.out.println( otherReference.getId() );

// Guess what will happen
System.out.println( reference.getId() ); // :S Throws NullPointerException because "reference" is pointing to NULL remember...

Это важно знать - когда больше нет ссылок на объект (в пример выше, когда reference и otherReference оба указывают на null), тогда объект «недоступен». Мы не можем работать с ним, поэтому этот объект готов к сбору мусора, и в какой-то момент VM освободит память, используемую этим объектом, и выделит другую.

14
задан Community 23 May 2017 в 12:24
поделиться

6 ответов

можно также записать что-то вроде этого:

let rec permutations list taken = 
  seq { if Set.count taken = List.length list then yield [] else
        for l in list do
          if not (Set.contains l taken) then 
            for perm in permutations list (Set.add l taken)  do
              yield l::perm }

аргумент 'списка' содержит все числа, которые Вы хотите переставить, и 'взятый' набор, который содержит числа, уже используемые. Функция возвращает пустой список когда все числа все взятые. Иначе это выполняет итерации по всем числам, которые все еще доступны, получает все возможные перестановки остающихся чисел (рекурсивно использующий 'перестановки') и добавляет текущее число каждому из них прежде, чем возвратиться (l:: перманент).

Для выполнения этого Вы дадите ему пустое множество, потому что никакие числа не используются вначале:

permutations [1;2;3] Set.empty;;
19
ответ дан 1 December 2019 в 07:41
поделиться

Мой последний лучший ответ

//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

работы функции перестановок путем построения дерева не, представляющего все возможные перестановки списка 'вещей', передал в, затем пересекая дерево для построения списка списков. Используя 'Seq' существенно улучшает производительность, поскольку это делает все ленивым.

второй параметр функции перестановок позволяет вызывающей стороне определять фильтр для 'сокращения' дерева прежде, чем генерировать пути (см. мой пример ниже, где я не хочу начальных нулей).

Некоторое использование в качестве примера: Node< 'a> универсально, таким образом, мы можем сделать перестановки 'чего-либо':

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] 

(Особая благодарность Tomas Petricek , любые приветствующиеся комментарии)

1
ответ дан 1 December 2019 в 07:41
поделиться

Мне нравится эта реализация (но не могу вспомнить ее источник):

let rec insertions x = function
    | []             -> [[x]]
    | (y :: ys) as l -> (x::l)::(List.map (fun x -> y::x) (insertions x ys))

let rec permutations = function
    | []      -> seq [ [] ]
    | x :: xs -> Seq.concat (Seq.map (insertions x) (permutations xs))
13
ответ дан 1 December 2019 в 07:41
поделиться

Взгляните на этот:

http://fsharpcode.blogspot.com/2010/04/permutations.html

let length = Seq.length
let take = Seq.take
let skip = Seq.skip
let (++) = Seq.append
let concat = Seq.concat
let map = Seq.map

let (|Empty|Cons|) (xs:seq<'a>) : Choice<Unit, 'a * seq<'a>> =
    if (Seq.isEmpty xs) then Empty else Cons(Seq.head xs, Seq.skip 1 xs)

let interleave x ys =
    seq { for i in [0..length ys] ->
            (take i ys) ++ seq [x] ++ (skip i ys) }

let rec permutations xs =
            match xs with
            | Empty -> seq [seq []]
            | Cons(x,xs) -> concat(map (interleave x) (permutations xs))
0
ответ дан 1 December 2019 в 07:41
поделиться

Решение Томаса довольно изящное: короткое, чисто функциональное и ленивое. Я думаю, это может быть даже хвостовая рекурсия. Кроме того, он производит перестановки лексикографически. Тем не менее, мы можем повысить производительность в два раза, используя императивное решение внутри компании, при этом открывая функциональный интерфейс извне.

Функция перестановок принимает общую последовательность e , а также общую функцию сравнения f: ('a ->' a -> int) и лениво дает неизменяемые перестановки лексикографически. Функционал сравнения позволяет нам генерировать перестановки элементов, которые не обязательно сопоставимы , а также легко определять обратный или индивидуальный порядок.

Внутренняя функция перестановка является императивной реализацией алгоритма, описанного здесь . Функция преобразования let comparer f = {new System.Collections.Generic.IComparer <'a> with member self.Compare (x, y) = fxy} позволяет нам использовать System.Array Перегрузка .Sort , которая выполняет пользовательскую сортировку поддиапазонов на месте с помощью 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' }

Теперь для удобства у нас есть следующее, где let flip fxy = fyx :

let permutationsAsc e = permutations compare e
let permutationsDesc e = permutations (flip compare) e
2
ответ дан 1 December 2019 в 07:41
поделиться

Если вам нужны разные перестановки (когда в исходном наборе есть дубликаты), вы можете использовать это:

let rec insertions pre c post =
    seq {
        if List.length post = 0 then
            yield pre @ [c]
        else
            if List.forall (fun x->x<>c) post then
                yield pre@[c]@post
            yield! insertions (pre@[post.Head]) c post.Tail
        }

let rec permutations l =
    seq {
        if List.length l = 1 then
            yield l
        else
            let subperms = permutations l.Tail
            for sub in subperms do
                yield! insertions [] l.Head sub
        }

Это прямой перевод из этого кода C #. Я открыт для предложений по более функциональному внешнему виду.

0
ответ дан 1 December 2019 в 07:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: