Pergunta

TL;DR: Is an implementation of the Heap's Algorithm adhering to the principals of functional programming possible, and are any implementations of it known? And by "adhering to the principals of functional programming", I mean mainly not relying on the mutation of an object to carry out the algorithm.


I decided to write a function that generates permutations of a list

(permutations [1 2 3])
=> [[1 2 3] [2 1 3] [3 1 2] [1 3 2] [2 3 1] [3 2 1]]

And after some quick searching, somewhat arbitrarily picked Heap's Algorithm.

The problem is, I'm writing in Clojure, trying to adhere to proper FP principals as close as possible, and the entire algorithm seems to hinge on mutating a "global" array while recursing. I worked on this for a couple of hours, and could not find a way of sanely getting the results passed "back up the stack", since the recursive calls just kind of float between mutations:

(defn- swap-v [v i1 i2]
  (let [x (get v i1)]
    (-> v
        (assoc i1 (get v i2))
        (assoc i2 x))))

(defn permutate [coll]
  (let [coll-atom (atom coll)
        result-atom (atom [coll])]
    ((fn rec [n]
       (when (> n 1)
         (let [n-even? (zero? (rem n 2))
               swap-pos #(if n-even? % 0)]

           (doseq [i (range (dec n))]
             ; Recursive call
             (rec (dec n))

             ; Mutate the "array" by swapping two elements
             (swap! coll-atom swap-v (swap-pos i) (dec n))

             ; Then mutate the results array so they can be returned later
             (swap! result-atom conj @coll-atom)) ; Mutate the

           ; ... And another recursive call down here
           (rec (dec n))))

      (count @coll-atom)))

    @result-atom)) ; Dereference the atom and return the results

I tried changing the doseq to a reduction, then I had the results returned nicely, but seemingly nothing to do with them.

I ended up giving up on a FP approach, wrote the above code, and tried to find an example of an FP approach to the algorithm to see where I had gone wrong. To my surprise, I wasn't able to find any. The closest I could find was a Haskell implementation, but honestly, I have no clue what most of the code is doing; it's pretty obscure. I see unsafe in a few places though, so I think they snuck some mutation in there.

Is this possible?

Note, I'm not looking for a review of the code here, since I'm aware this is very imperative-styled, and I already has a review request open.

Nenhuma solução correta

Licenciado em: CC-BY-SA com atribuição
Não afiliado a cs.stackexchange
scroll top