8

I'm attempting to create a list of lists to imitate the functionality of a 2D array in Dart, and I was having trouble for a while figuring out how to make it work.

I originally used List.forEach in order to create the lists and fill them, but after each loop, it's as if I never created the lists. Here's what that code looked like:

currentMap = new List<List<int>>(height);
currentMap.forEach((e) {
    e = new List<int>(width);
    e.forEach((f) {
        f = 1;
    });
});

Printing currentMap resulted in a list of null. Here's what I have now:

currentMap = new List<List<int>>(height);
for(int i = 0; i < height; i++) {
    currentMap[i] = new List<int>(width);
    for(int j = 0; j < width; j++) {
        currentMap[i][j] = 1;
    }
}

This works exactly as I expected it to.

I understand that this is probably a very basic issue, and I am assuming that forEach does not allow you to modify the element, but I wanted some confirmation on that, and the Dart API docs do not specify except with the phrase "apples the function f to each element..." which may just not be clear to me.

Also, I am aware of things like the List.filled() constructor -- this is just how I am starting to build towards other functionality.

EDIT: Okay, I think I understand now. Arguments are "pass-by-sharing" which means that a copy of a reference to the object is made. This means that you can modify a member of a mutable object that is pointed to by the argument (as follows):

void function(MyObject o) {
    o.x = 5;
}

but trying to change what o points to will not change the argument after exiting the function, such as this:

void function(MyObject o) {
    MyObject p = new MyObject();
    o = p;
}
k_Reign
  • 83
  • 1
  • 5

3 Answers3

6

Dart does not have variable references, all elements are passed as a reference to an object, not to a variable. (In other words, Dart is purely "call-by-sharing" like both Java and JavaScript).

That means that the e parameter to the forEach callback is just a normal local variable, and assigning to it has no effect outside the callback. The same goes for iterators: they return the value in the iterable, but it has no reference back to the iterable after that.

The double loop is what you want when you do "read-modify-write". You read a value from the list, modify it and store it back into the list using the list's index-set operator.

I know you know about List.filled, but just for other readers, I'll give the one-liner to create the two-dimensional array initialized to 1 values:

currentMap = new List.generate(height, (_) => new List.filled(width, 1));
lrn
  • 64,680
  • 7
  • 105
  • 121
  • Okay, so let's see if I have this right: primitive types are pass-by-value, and objects are pass-by-reference. In my forEach, I AM creating a list for each element because the lists are objects so passed by reference, but the inner loop is not modifying the values since they are ints. Does that sound right? I really appreciate your response by the way; it makes a lot of sense. – k_Reign Mar 06 '14 at 16:29
  • There is no pass-by-value in Dart only pass-by-reference. But you do not have the reference in the list that points to the value, e is a copy of the reference. You can assign a new value to e, but that doesn't affect the list. – Günter Zöchbauer Mar 06 '14 at 17:15
  • I appreciate all the comments. Could you explain what you mean by "reference"? When I hear that I think something akin to pointers, so I'm thinking that the original would be modified but clearly that is not the case. – k_Reign Mar 06 '14 at 17:50
  • 1
    All values in dart are objects, there is no "primitive types" that are not objects - everything has methods, even integers. Dart is "pass-by-(object-)sharing" - which basically means "pass-by-value where values are object references". It is exactly the same as Java and JavaScript. You will find threads discussing what it's called, but the behavior is clear (http://stackoverflow.com/questions/40480/is-java-pass-by-reference). It is not what is traditionally called "pass-by-reference", where you pass references to *variables*, which can then be modified from the called function. – lrn Mar 06 '14 at 18:39
  • 1
    It is not because the values are ints that modifying 'e' doesn't change the content of the list. It is because `e` is just a variable, it does not correspond to an entry of the list. It's merely, temporarily, bound to the same value as a list entry. Changing the value of `e` doesn't change anything else. No more than if you did: `var z = e; z = newValue;`. – lrn Mar 06 '14 at 18:50
3

This is because e is only a reference to the List in currentMap not to a currentMap item.
You update a copy of the reference to a List, but this reference has no connection to currentMap.
After the loop, e just gets garbage collected.

Darshan Rivka Whittle
  • 32,989
  • 7
  • 91
  • 109
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • If that is the case then why does this statement work? e = new List(width); I understand that f is just a copied value but it does seem as though I can modify the value of e. Thanks for the response! – k_Reign Mar 06 '14 at 17:11
  • As lrn wrote. e and f are local variables that are initialized with a value that points to a list item. You can reassign to them what you want (the analyzer shouts error if the type doesn't fit though) but it doesn't change to which value the list item points to. – Günter Zöchbauer Mar 06 '14 at 17:19
  • Okay, that makes sense and I think I was mistaken in saying the statement affected the list. I'll have to play around with this when I get a chance. Thanks for the clarification! – k_Reign Mar 06 '14 at 17:48
  • Glad I could help - was also some practice in english because it was not so easy to explain ;-) – Günter Zöchbauer Mar 06 '14 at 17:53
1

This is because of how an Enumerator works and I believe because the Enumerator is immutable You can't modify the collection items when it's being processed in a foreach... removing members and such.

See also:

http://msdn.microsoft.com/en-us/library/ttw7t8t6.aspx

What is the best way to modify a list in a 'foreach' loop?

Community
  • 1
  • 1
Thomas Taylor
  • 555
  • 4
  • 12
  • Thanks for the quick response and for the links. I've found this line in Dart's API docs for Iterable: "It is generally not allowed to modify such collections while they are being iterated." Which does not explain why it let me do it in the first place, but does explain that maybe it wasn't a great idea to try to do in the first place. – k_Reign Mar 05 '14 at 22:15