Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How would be a functional approach to shifting certain array elements?

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!

like image 244
Germán Avatar asked Feb 11 '10 14:02

Germán


3 Answers

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
}
like image 193
Geoff Reedy Avatar answered Sep 21 '22 06:09

Geoff Reedy


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

like image 22
oxbow_lakes Avatar answered Sep 23 '22 06:09

oxbow_lakes


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
}
like image 45
huynhjl Avatar answered Sep 21 '22 06:09

huynhjl