4

I'm would like to create generic (invariant) method in Scala which copies elements from source list to destination list. In Java there is copy method in java.util.Collections (see http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Collections.html#copy%28java.util.List,%20java.util.List%29). I know in Scala List is immutable object so i would like to create and return new list.

I've written the following code:

def copy[T](dest:List[T], src:List[T]):List[T] = {
    if(dest.length<src.length) throw new Exception("IndexOutOfBoundsException")
    else if(src==Nil) dest
    else {
        var ret = dest
        ret = dest.updated(0, src.first)
        var i=1
        val f:(T=>Unit) = a => {
            if(i<src.length) ret=ret.updated(i, src(i))
            i+=1
            ()
        }
        dest.foreach(f)
        ret
    } 
}

But I think it could be written better. Could you help me to write better code? Thanks in advance.

EDITED: Maybe I expressed unclear what I want to do. I have two lists (scala.collection.immutable.List), e.g. src (length=x) and dest(length=y>=x). I would like to replace first x elements of dest list with elements from src list.

sarveshseri
  • 13,738
  • 28
  • 47
Paul
  • 1,917
  • 4
  • 17
  • 16
  • 3
    Do you mean `scala.collection.immutable.List`? It is immutable. No need to copy them. – senia Dec 10 '12 at 12:29
  • According to your edit: you want to do it in-place? (e.g. will you be unhappy if there will be third list, say `updated` as a result of such replacement?) – om-nom-nom Dec 10 '12 at 13:34
  • It would be the best to do it in-place. I've got solution but if exists better one, i'm interested to see it. – Paul Dec 10 '12 at 13:37
  • 1
    Do not use indexed access like `src(i)` on `List`. `List` in scala is [Linked List](http://en.wikipedia.org/wiki/Linked_list). Indexed access on it is very slow. – senia Dec 10 '12 at 13:43
  • Thank you, didn't think about it earlier. – Paul Dec 10 '12 at 13:55

5 Answers5

8

Do you mean scala.collection.immutable.List? It is immutable. No need to copy them. Immutable means that nothing can change it, so you can use it in different threads.

Generic way of creating collections in scala is builder. You can get one from CanBuildFrom object. Alternatively you can get it from genericBuilder method of collection instance.

scala> val list = List(1, 2, 3)
list: List[Int] = List(1, 2, 3)

scala> val b = list.genericBuilder[Int]
b: scala.collection.mutable.Builder[Int,List[Int]] = ListBuffer()

scala> list.foreach{ b += _ }

scala> val result = b.result // this code is useless. `val result = list` is enough
result: List[Int] = List(1, 2, 3)

If you want to create new collection of different type based on existing collection, you can use collection.breakOut methid like this:

scala> val list = List('a', 'b', 'c')
list: List[Char] = List(a, b, c)

scala> val result: String = list.map{identity}(collection.breakOut)
result: String = abc

Upd

require(src.length <= dest.length, "IndexOutOfBoundsException")
src ++ dest.drop(src.length)
Community
  • 1
  • 1
senia
  • 37,745
  • 4
  • 88
  • 129
  • 1
    For further details on BreakOut and CanBuildFrom [see this post](http://stackoverflow.com/a/1716558/298389) – om-nom-nom Dec 10 '12 at 13:09
  • Thanks, good to know that. But I would like to do something other. Maybe I explained not so good, I add something to my question. – Paul Dec 10 '12 at 13:28
3

You are thinking far too procedurally, say what you want not how to do it...

how about:

val src = List(1,2,3)
val dest = src map {x => x}

if you really want to make a function of it

def copy[T](src: List[T]): List[T] = src map {x => x}

in response to OP's update:(which has also been proposed by others)

def copy[T](src: List[T], dest: List[T]): List[T] = src ++ dest.drop(src.length)
Robert
  • 8,406
  • 9
  • 38
  • 57
  • Thanks also. But I would like to do something other. Maybe I explained not so good, I add something to my question. – Paul Dec 10 '12 at 13:29
3

If you want to get an updated list you can use map on your list. Map works by applying a function to each element in the list, and returning updated list.

http://www.brunton-spall.co.uk/post/2011/12/02/map-map-and-flatmap-in-scala/

Mixabuben
  • 123
  • 3
  • 5
2

You could use:

if(dest.length <= src.length) dest ::: src.drop(dest.length)
else dest.dropRight(dest.length - src.length) //or throw exception...
Echilon
  • 10,064
  • 33
  • 131
  • 217
gilad hoch
  • 2,846
  • 2
  • 33
  • 57
1

Maybe you want something like

 def copy[T](dest: Seq[T], src: Seq[T]): Seq[T] = {
   require(dest.length >= src.length)
   src ++ (dest drop src.length)
 }

I generalized to Seqs, but it works on Lists, of course

The require method throws IllegalArgumentException if not fulfilled at runtime

Then you need only append the last (y-x) elements of the destination list to to the source list (where x = src.length; y = dest.length)

You do this by dropping x elements from dest and appending the remaining to src.

This is what you get from the REPL

scala> val src = List(1, 2, 3, 4)
src: List[Int] = List(1, 2, 3, 4)

scala> val dst = List(10, 20)
dst: List[Int] = List(10, 20)

scala> val dst2 = List(10, 20, 30, 40, 50, 60)
dst2: List[Int] = List(10, 20, 30, 40, 50, 60)

scala> copy(dst, src)
java.lang.IllegalArgumentException: requirement failed
        at scala.Predef$.require(Predef.scala:221)
        at .copy(<console>:8)
        at .<init>(<console>:11)
        at .<clinit>(<console>)
        at .<init>(<console>:7)
        at .<clinit>(<console>)
        <...>

scala> copy(dst2, src)
res1: Seq[Int] = List(1, 2, 3, 4, 50, 60)
pagoda_5b
  • 7,333
  • 1
  • 27
  • 40
  • Same solution proposed from @senia in her update... Her answer was bogged down in the main answer and I didn't saw that at first. – pagoda_5b Dec 10 '12 at 14:58