【问题标题】:List.forEach unable to modify element?List.forEach 无法修改元素?
【发布时间】:2014-03-05 21:58:28
【问题描述】:

我正在尝试创建一个列表列表来模仿 Dart 中二维数组的功能,但我有一段时间无法弄清楚如何使其工作。

我最初使用List.forEach 来创建列表并填充它们,但在每个循环之后,就好像我从未创建过列表一样。这段代码是这样的:

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

打印currentMap 生成null 列表。这是我现在拥有的:

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;
    }
}

这完全符合我的预期。

我知道这可能是一个非常基本的问题,并且我假设 forEach 不允许您修改元素,但我想对此进行一些确认,并且 Dart API 文档除了使用短语“将函数 f 应用于每个元素......”我可能不太清楚。

另外,我知道List.filled() 构造函数之类的东西——这正是我开始构建其他功能的方式。

编辑:好的,我想我现在明白了。参数是“传递共享”,这意味着对对象的引用进行了复制。这意味着您可以修改参数指向的可变对象的成员(如下所示):

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

但是尝试改变o指向的东西不会在退出函数后改变参数,比如:

void function(MyObject o) {
    MyObject p = new MyObject();
    o = p;
}

【问题讨论】:

    标签: arrays foreach dart


    【解决方案1】:

    Dart 没有变量引用,所有元素都作为引用传递给对象,而不是变量。 (换句话说,Dart 就像 Java 和 JavaScript 一样纯粹是“共享调用”。

    这意味着forEach 回调的e 参数只是一个普通的局部变量,在回调之外分配给它没有任何影响。迭代器也是如此:它们返回可迭代对象中的值,但之后不再引用返回可迭代对象。

    双循环是您执行“读取-修改-写入”时想要的。您从列表中读取一个值,对其进行修改并使用列表的索引集运算符将其存储回列表中。

    我知道你知道List.filled,但仅针对其他读者,我将给出创建初始化为1 值的二维数组的单行代码:

    currentMap = new List.generate(height, (_) => new List.filled(width, 1));
    

    【讨论】:

    • 好的,让我们看看我是否有这个权利:原始类型是按值传递的,而对象是按引用传递的。在我的 forEach 中,我正在为每个元素创建一个列表,因为这些列表是通过引用传递的对象,但内部循环不会修改这些值,因为它们是整数。听起来对吗?顺便说一句,我非常感谢您的回复;这很有意义。
    • Dart 中没有按值传递,只有按引用传递。但是列表中没有指向该值的引用,e 是引用的副本。您可以为 e 分配一个新值,但这不会影响列表。
    • 感谢所有的 cmets。你能解释一下“参考”是什么意思吗?当我听到我认为类似于指针的东西时,我认为原件会被修改,但显然情况并非如此。
    • dart 中的所有值都是对象,没有不是对象的“原始类型”——一切都有方法,甚至整数。 Dart 是“pass-by-(object-)sharing” - 这基本上意味着“值是对象引用的值传递”。它与 Java 和 JavaScript 完全相同。您会发现线程在讨论它的名称,但行为很明确 (stackoverflow.com/questions/40480/is-java-pass-by-reference)。这不是传统上所谓的“按引用传递”,您将引用传递给变量,然后可以从被调用的函数中对其进行修改。
    • 不是因为值是整数,所以修改'e'不会改变列表的内容。这是因为e 只是一个变量,它不对应于列表的条目。它只是暂时绑定到与列表条目相同的值。更改 e 的值不会改变其他任何内容。不超过你这样做:var z = e; z = newValue;.
    【解决方案2】:

    这是因为e 只是对currentMapList 的引用,而不是对currentMap 项目的引用。
    您将引用的副本更新为 List,但此引用与 currentMap 没有任何关联。
    在循环之后,e 会被垃圾回收。

    【讨论】:

    • 如果是这样,那么为什么这个语句有效? e = 新列表(宽度);我知道 f 只是一个复制的值,但似乎我可以修改 e 的值。感谢您的回复!
    • 正如 lrn 所写。 e 和 f 是使用指向列表项的值初始化的局部变量。您可以为他们重新分配您想要的内容(如果类型不合适,分析器会喊出错误),但它不会更改为列表项指向的值。
    • 好的,这是有道理的,我认为我说该声明影响了列表是错误的。当我有机会时,我将不得不解决这个问题。感谢您的澄清!
    • 很高兴我能帮上忙 - 也是用英语练习,因为它不太容易解释;-)
    【解决方案3】:

    这是因为 Enumerator 的工作方式,我相信因为 Enumerator 是不可变的,当在 foreach 中处理集合项时,您无法修改集合项...删除成员等。

    另见:

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

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

    【讨论】:

    • 感谢您的快速响应和链接。我在 Dart 的 Iterable 的 API 文档中找到了这一行:“通常不允许在迭代时修改此类集合。”这并不能解释为什么它让我一开始就这样做,但确实解释了可能一开始就尝试这样做不是一个好主意。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-16
    • 2013-10-17
    • 1970-01-01
    相关资源
    最近更新 更多