2

Here is a simple code to generate all permutations, based on implementation found here: All permutations of a list

let concatElement element sequence =
    seq {
        yield element
        yield! sequence
    }

let rec permute (choices : 'a seq)=
    seq {
        if Seq.isEmpty choices then
            yield Seq.empty
        else
            for choice in choices do
                let remaining = choices |> Seq.where (fun el -> el <> choice)
                yield concatElement choice (permute remaining)
    }

I get a compile-time error "The resulting type would be infinite when unifying ''a' and 'seq<'a>'" on "yield concatElement choice (permute remaining)"

What is wrong here?

Community
  • 1
  • 1
Lutosław
  • 606
  • 7
  • 24
  • 2
    What should the return type be? Assuming it should be `seq>` you need the first argument to `concatElement` on the last line to be a `seq<'a>` but `choice` is an `'a`. – Lee Nov 02 '16 at 14:14
  • concatElement just appends one element to a sequence and returns seq<'a>, so yield concatElement should yield a seq<'a>, which in result produces seq>. What am I missing? Or maybe I should pass a "head" around and concat two sequences? – Lutosław Nov 02 '16 at 14:25
  • 3
    If `permute` returns a `seq>` then `concatElement choice (permute remaining)` is trying to concat an `'a` and a `seq>` which is a type error. If you add an explicit return type to `permute` you might get a more useful error message. – Lee Nov 02 '16 at 14:30
  • I've cited another implementation but ignored a nested loop totally. Shame on me. I would prefer to change it to tail recursion but it doesn't seem to be straightforward – Lutosław Nov 02 '16 at 14:56

2 Answers2

4

You're trying to yield an 'a seq as a single element of a sequence which has type 'a seq. For that to work, then 'a must be the same type as 'a seq, and so on, so the type is "infinite" - you'd have to have nested them infinitely for it to "work".

I see in your answer that you recognised you have to loop through the sequence elements somehow. You can, as you have, use the for .. in syntax to iterate through, or F# gives you the yield! operator which actually does this for you. In this spirit, your answer could be written as

let rec permute (choices : 'a seq) (permBuilder: 'a seq) : seq<seq<'a>>= seq {
    if Seq.isEmpty choices then
        yield permBuilder
    else
        for choice in choices do
            let remaining = choices |> Seq.where (fun el -> el <> choice)
            let newBuilder = concatElement choice permBuilder 
            yield! permute remaining newBuilder }

Or, alternatively, we can use the Seq.collect function to automatically gather a sequence of sequences (of sequences!) for us, and we're left with nice functional-style code with no explicit iterators:

let prepend element sequence = seq { yield element; yield! sequence }

let rec permute input =
    if Seq.length input <= 1 then Seq.singleton input
    else
        let without element = Seq.where ((<>) element) input
        let permutations element = permute (without element) |> Seq.map (prepend element)
        Seq.collect permutations input

Something to consider, though: what happens if you have two elements which aren't distinct? For example, what happens if you try to get the permutations of [1; 1; 2]? Your current method can't handle this neatly - even if you improved the without closure, you'd get duplicate answers.

Jake Lishman
  • 907
  • 7
  • 18
1

I think I got it

let concatElement element sequence =
    seq {
        yield element
        yield! sequence
    }

let rec permute (choices : 'a seq) (permBuilder: 'a seq) : seq<seq<'a>>=
    seq {
        if Seq.isEmpty choices then
            yield permBuilder
        else
            for choice in choices do
                let remaining = choices |> Seq.where (fun el -> el <> choice)
                let newBuilder = concatElement choice permBuilder 
                for perm in permute remaining newBuilder do
                    yield perm

As Lee've has commented, I tried to concat seq<'a> with seq< seq<'a>>. My code lacked a loop over permutations.

Lutosław
  • 606
  • 7
  • 24