Jan
Jan

Reputation: 1612

scala list manipulation: is this good scala?

I wrote some code in scala to resolve relative (file)paths, see code below. I store the paths as lists of strings, and when I calculate a relative path I use a mutable list variable in which I store modified copies of the list on which I work.

I have a gut feeling that this is probably not the best way of doing it, or is it? Should I use a mutable list and manipulate that? Or should I only use immutable Lists, no vars at all and a functional version? How should the method 'relative' be written?

class PathInfo(e: List[String]) {

    val elements=e;

    def relative(s : String) :PathInfo= relative(PathInfo.fromString(s));

    def relative(that : PathInfo) : PathInfo = {
        var list : List[String]=List();
        for (item <- elements) {
            list = list :+ item;
        }
        for (item <- that.elements) {
            item match {
                case "." => ;
                case ".." => list = list dropRight(1);
                case other => list = list :+ other;
            }
        }
        new PathInfo(list);
    }

    override def toString : String = {
        elements.mkString("/");
    }

}

object PathInfo {
    def fromString(s : String) : PathInfo={
        new PathInfo(List.fromString(s,'/'));
    }
}

Upvotes: 1

Views: 1038

Answers (2)

Kris Nuttycombe
Kris Nuttycombe

Reputation: 4580

I'm just going to make a minor edit to David's suggestion; I've found that I prefer deconstructing the elements of the tuple in the foldLeft directly:

val list = that.elements.foldLeft(this.elements) { 
  case (xs, ".")  => xs
  case (xs, "..") => xs init
  case (xs, x)    => xs :+ x
}

Now, there's a puzzler here. This works, and does so in a lovely fashion... but, can you figure out why a PartialFunction[(List[String], String), List[String]] is being promoted to a Function2[List[String], String, List[String]]? :)

Upvotes: 3

David Winslow
David Winslow

Reputation: 8590

In general, loops of the form:

var foo = initialFoo
for (x <- xs) foo = f(foo, x)

can be replaced with a foldLeft:

val foo = xs.foldLeft(initialFoo)(f)

In this specific case the foldLeft version would look like:

val list = that.elements.foldLeft(this.elements) { (xs, x) => 
  x match {
    case "." => xs
    case ".." => xs init
    case other => xs :+ other
  }
}

By the way, there's no need to iterate over the elements list to copy it for the initial value of list - it's an immutable List so it is safe to share.

Upvotes: 5

Related Questions