Below is an implementation of a function that returns the lexographically next permutation. This is useful in one of the Euler problems.
It’s written to work on Strings (which I needed for that). However, it should work on any indexed sequence of comparable values. I’ve tried generalising it by changing the two occurrences of String to IndexedSeq[Char], but this gets an error:
euler-lib.scala:26: error: type mismatch;
found : IndexedSeq[Char]
required: String
((n.slice(pivot+1, successor):+ n(pivot)) + n.drop(successor+1)).reverse
^
Why has the type inferencer inferred String there? I don’t seem to have done any operation that requires a String?
And can I make it more general still by having IndexedSeq[“something-comparable”]? I’ve not been able to make this work.
// return the lexographically next permutation to the one passed as a parameter
// pseudo-code from an article on StackOverflow
def nextPermutation(n:String):String = {
// 1. scan the array from right-to-left
//1.1. if the current element is less than its right-hand neighbor,
// call the current element the pivot,
// and stop scanning
// (We scan left-to-right and return the last such).
val pivot = n.zip(n.tail).lastIndexWhere{ case (first, second) => first < second }
//1.2. if the left end is reached without finding a pivot,
// reverse the array and return
// (the permutation was the lexicographically last, so its time to start over)
if (pivot < 0) return n.reverse
//2. scan the array from right-to-left again,
// to find the rightmost element larger than the pivot
// (call that one the successor)
val successor = n.lastIndexWhere{_ > n(pivot)}
//3. swap the pivot and the successor, and
//4. reverse the portion of the array to the right of where the pivot was found
return (n.take(pivot) :+ n(successor)) +
((n.slice(pivot+1, successor):+ n(pivot)) + n.drop(successor+1)).reverse
}
The method
+inIndexedSeqis used to produce a new sequence containing one additional given element but you want to produce one containing an additional sequence. The method for this is++thus your last line must look like this:You are seeing this strange compiler message about a
Stringbeing expected because+‘s signature does not match and thus an explicit conversion used for String concatenation kicks in (this conversion is there because it lets you write something likeList(8) + " Test").EDIT: Generalization over sequence types of ordered elements:
As I said in the comments, generalization over sequences is a bit more complicated. In addition to your element type
Ayou will need another typeCC[X] <: SeqLike[X,CC[X]]that represents the sequence. NormallyC <: SeqLike[A,C]would be sufficient but the type inferencer does not like that one (you would always need to pass the types ofAandCwhen calling that method).If you just change your signature that way the compiler will complain that it requires an implicit
CanBuildFrom[CC[A],A,CC[A]]parameter as that one is needed e.g. by thereversemethod. That parameter is used to build one sequence type from another one – just search the site to see some examples of how it is used by the collections API.The final result would look like this:
This way you get a
Vector[Int]if you passed one to the method and aList[Double]if you passed that to the method. So what aboutStrings? Those are not actual sequences but they can be implicitly converted into aSeq[Char]. It is possible alter the definition of that method expect some type that can be implicitly converted into aSeq[A]but then again type inference would not work reliably – or at least I could not make it work reliably. As a simple workaround you could define an additional method forStrings: