I have a Scala app with a list of items with checkboxes so the user select some, and click a button to shift them one position up (left). I decided to write a function to shift elements of some arbitrary type which meet a given predicate. So, if you have these elements:
a b c D E f g h I
and the predicate is "uppercase characters", the function would return this:
a b D E c f g I h
In short, any sequence of contiguous elements that meet the predicate are swapped with the single element at the left of it.
I came up with the following ugly imperative implementation. I would like to see a nice, and hopefully readable, functional solution.
def shiftUp[T](a:Array[T], shiftable: T => Boolean) = {
val s = new Array[T](a.length)
var i = 0
var j = 0
while(i < a.length)
{
if(!shiftable(a(i)) && i < a.length - 1 && shiftable(a(i+1)))
{
var ii = i + 1
while(ii < a.length && shiftable(a(ii)))
{
s(j) = a(ii)
ii = ii+1
j = j+1
}
s(j) = a(i)
i = ii
}
else
{
s(j) = a(i)
i = i+1
}
j = j+1
}
s
}
EDIT: Thanks all, I hope you have enjoyed the exercise!
Here's a purely functional implementation
def shiftElements[A](l: List[A], pred: A => Boolean): List[A] = {
def aux(lx: List[A], accum: List[A]): List[A] = {
lx match {
case Nil => accum
case a::b::xs if pred(b) && !pred(a) => aux(a::xs, b::accum)
case x::xs => aux(xs, x::accum)
}
}
aux(l, Nil).reverse
}
And here's one that uses mutability on the inside to be faster
import scala.collection.mutable.ListBuffer
def shiftElements2[A](l: List[A], pred: A => Boolean): List[A] = {
val buf = new ListBuffer[A]
def aux(lx: List[A]) {
lx match {
case Nil => ()
case a::b::xs if pred(b) && !pred(a) => {
buf.append(b)
aux(a::xs)
}
case x::xs => {
buf.append(x)
aux(xs)
}
}
}
aux(l)
buf.toList
}
You could probably do this via a foldLeft
(also known as /:
):
(str(0).toString /: str.substring(1)) { (buf, ch) =>
if (ch.isUpper) buf.dropRight(1) + ch + buf.last else buf + ch
}
It needs work to handle the empty String but:
def foo(Str: String)(p: Char => Boolean) : String = (str(0).toString /: str.substring(1)) {
(buf, ch) => if (p(ch) ) buf.dropRight(1) + ch + buf.last else buf + ch
}
val pred = (ch: Char) => ch.isUpper
foo("abcDEfghI")(pred) //prints abDEcfgIh
I'll leave it as an exercise as to how to modify this into the array-based solution
This is basically an imperative algorithm with a functional style.
def shifWithSwap[T](a: Array[T], p: T => Boolean) = {
def swap(i:Int, j:Int) = {
val tmp = a(i); a(i) = a(j); a(j) = tmp
}
def checkAndSwap(i:Int) = i match {
case n if n < a.length - 1 && !p(a(i)) && p(a(i+1)) => swap(i, i+1)
case _ =>
}
(0 until a.length - 1) map checkAndSwap
a
}
It modifies the Array in place, with a side effect. I think it really does it like the version in the question, except it's easier to read. Imperative does not have to be ugly...
Edit: darn, couldn't fall asleep until I wrote this down (same as above, just more compact):
def shift[T](a: Array[T], p: T => Boolean) = {
for (i <- 0 until a.length - 1; if !p(a(i)) && p(a(i+1))) {
val tmp = a(i); a(i) = a(i+1); a(i+1) = tmp // swap
}
a
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With