2

Example

This test fails because each does not modify the list's elements.

void test_each_DoesNotModifyListElements() {
    List<String> list = ["a", "b", "c"]
    list.each { it =  it + "z" }
    assert ["az", "bz", "cz"] == list
}

This test passes because collect returns a new list with modified elements.

void test_collect_ReturnsNewListWithModifiedElements() {
    List<String> list = ["a", "b", "c"]
    list = list.collect{ it + "z" }
    assert ["az", "bz", "cz"] == list
}

Assumption

At this point, I am assuming that each does not modify the list's elements because I have written the tests below and I am assuming that that's what the tests are telling me. Please correct me if I am wrong.

void test_each_DoesNotModifyListElements1() {
    List<String> list = ["a", "b", "c"]
    list.each { it + "z" }
    assert ["az", "bz", "cz"] == list
}

void test_each_DoesNotModifyListElements2() {
    List<String> list = ["a", "b", "c"]
    list.each { it =  it + "z" }
    assert ["az", "bz", "cz"] == list
}

void test_each_DoesNotModifyListElements3() {
    List<String> list = ["a", "b", "c"]
    list = list.each { it =  it + "z" }
    assert ["az", "bz", "cz"] == list
}

Question

So the question is: as a Groovy beginner, how should I have known beforehand, meaning before writing tests and googling, that each does not change the list's element?

By reading the documentation?

Groovy each documentation: Iterates through an aggregate type or data structure, passing each item to the given closure. Custom types may utilize this method by simply providing an "iterator()" method. The items returned from the resulting iterator will be passed to the closure.

Oftentimes, I end up spending a lot of time trying things out and googling for answers. Shouldn't there be a more efficient way? Is there?

Community
  • 1
  • 1
Lernkurve
  • 20,203
  • 28
  • 86
  • 118
  • 1
    Would you expect a `for` loop to be able to change a List's elements in Java for example? – tim_yates May 08 '13 at 10:27
  • @tim_yates: I'd say... yes? Do you mean this? `void test_for_loop() { List list = ["a", "b", "c"] for (int i = 0; i < list.size(); i++) { list[i] = list[i] + "z" } assert ["az", "bz", "cz"] == list }` – Lernkurve May 08 '13 at 10:31
  • Ahhh, but there you are altering the `list` inside the loop, in your `each` examples above, you make no reference to the `list` itself – tim_yates May 08 '13 at 10:32
  • You can do that with `each` as well if you want `list = [ 'a', 'b', 'c' ] ; list.eachWithIndex { it, idx -> list[ idx ] = it + 'z' }` but using `collect` is much nicer – tim_yates May 08 '13 at 10:33
  • @tim_yates: Isn't `it` a reference to the String object `"a"`? Same as `list[0]` is a reference to the String object `"a"`? `it` in the first iteration of `each` is not identical to `list[0]`? – Lernkurve May 08 '13 at 10:37
  • 2
    Identical value, but different semantics as in the `each` the value has been passed as a parameter into the `each` closure, so any changes will be lost (as with any method call in Java). With the for loop, you are modifying the actual contents of the list at position `[0]`. – tim_yates May 08 '13 at 10:39
  • @tim_yates: Ah, `it` is a value type and `list[0]` is a reference type. Correct? – Lernkurve May 08 '13 at 10:46
  • Yeah, Groovy (like Java) is Pass by Value (though I always try to avoid the debate as you get [bogged down in the semantics of what reference means](http://stackoverflow.com/questions/40480/is-java-pass-by-reference)) ;-) – tim_yates May 08 '13 at 10:48
  • @tim_yates: Great link, thanks. My brain exploded after reading "as the object reference is passed by value", then things became clearer. Thank you very much for taking the time clearing things up! – Lernkurve May 08 '13 at 10:55
  • Hehe, yeah that's why I try and steer clear of the "pass by" discussions ;-) No worries, glad if I helped a bit and good luck with Groovy! :-) – tim_yates May 08 '13 at 10:56

2 Answers2

5

Well, that is not far from Java/Groovy programming basics. There are Objects, and there are Variables. Variable can hold a "link" to the object, called a reference. So basically, using a variable you can access the real object stored in depths of computer memory.

Whenever you alter the Variable (that is, assign to it something), you never modify the real object. Instead, the variable begins to point to some different Object.

Now, looking at documentation again:

Iterates through an aggregate type or data structure, passing each item to the given closure.

That's all it does. It gives you a variable pointing to each item. If you overwrite the local variable you had been given, the variable starts to point to other object and that's it. The list still has the reference to the old object.

That information is the most basical knowledge required to program in Java and similar languages. Usually it is explained at the first pages of manuals/documentations. And because it is that basic, nobody bothers explaining it over and over again for each function where you have some kind of parameter.

bezmax
  • 25,562
  • 10
  • 53
  • 84
  • Highlighting the relevant documentation part and stating "basic knowledge" therefore not mentioned repetitively all over the place, made it clear for me. Thank you! – Lernkurve May 08 '13 at 10:48
1

@Max has provided an excellent answer above. My 2 cents to go with it. The variables or references are actually pass by value in java. Understanding this is also important. Please note, the object itself is not copied only the reference to it is copied.

Now lets come back to the list. It has references to all the objects within it. When you call each on the list, a new reference is created pointing to the same object and passed to the each method. So even if you make this newly created reference variable point to a different object, the reference in the list is still unaltered and pointing to the same old object.

Abe
  • 8,623
  • 10
  • 50
  • 74