Array的“每个”或“某些”副作用是否有害?


9

我一直被教导,在某种if情况下产生副作用是不好的。我的意思是;

if (conditionThenHandle()) {
    // do effectively nothing
}

...而不是

if (condition()) {
    handle();
}

...我知道,我的同事很高兴,因为我不这样做,我们每个星期五的17:00回家,每个人都有一个快乐的周末。

现在,ECMAScript5推出类似的方法every(),并some()Array了,我发现它们非常有用。他们是清洁比for (;;;)的,再给你一个范围,并且使元素由变量访问。

但是,在验证输入时,我经常发现自己在条件中使用every/ some来验证输入,然后再次在正文中使用every/ 将输入转换为可用模型。some

if (input.every(function (that) {
    return typeof that === "number";
})) {
    input.every(function (that) {
        // Model.findById(that); etc
    }
} else {
    return;
}

...当我想做的

if (!input.every(function (that) {
    var res = typeof that === "number";

    if (res) {
        // Model.findById(that); etc.
    }

    return res;
})) {
    return;
}

...会给我带来不良影响的副作用if

相比之下,这是带有旧代码的代码for (;;;)

for (var i=0;i<input.length;i++) {
    var curr = input[i];

    if (typeof curr === "number") {
        return;
    }

    // Model.findById(curr); etc.
}

我的问题是:

  1. 这绝对是不好的做法吗?
  2. 上午我(MIS | AB)使用someevery我可以用for(;;;)这个?)
  3. 有没有更好的方法?

3
每一个查询以及filter,map和reduce都是查询,如果您滥用它们,它们没有副作用。
本杰明·格伦鲍姆

@BenjaminGruenbaum:难道这不是使他们更无牙吗?9/10,如果我使用some,我想对元素做某事,如果我使用every,我想对所有这些元素做某事... some并且every不要让我访问该信息,所以我不能使用它们,否则我必须添加副作用。
艾萨克(Isaac)

不。当我提到副作用时,我的意思是说如果不是在身体的头部。在身体内部,您可以根据需要进行任何修改。只是不要在传递给某些/时间的回调中更改对象。
本杰明·格伦鲍姆

@BenjaminGruenbaum:但这正是我的观点。如果使用some在我的if条件来确定所述阵列中的某些元件是否表现出一定的属性,9/10我需要操作在我的该元件if主体; 现在,因为some没有告诉我哪个元素展示了该属性(只是“一个完成了”),所以我可以在主体中some 再次使用(O(2n)),可以在if条件中执行操作(这很不好,因为它在头部会产生副作用。
艾萨克(Isaac)

…… every当然也是如此。
艾萨克(Isaac)

Answers:


8

如果我正确理解了您的观点,那么您似乎在滥用或滥用everysome但是如果您想直接更改数组的元素,这是不可避免的。如果我错了,请纠正我,但是您要尝试做的是找出序列中的某些或每个元素是否表现出一定条件,然后修改这些元素。另外,您的代码似乎正在对所有项目应用某些内容,直到您发现未通过该谓词的项目为止,而且我认为这不是您要执行的操作。无论如何。

让我们以第一个示例(稍作修改)为例

if (input.every(function (that) {
    return typeof that === "number";
})) {
    input.every(function (that) {
        that.foo();
    }
} else {
    return;
}

实际上,您在这里所做的事情与某些/每一个/地图/减少/过滤器/等概念的精神有些背离。 Every并不是要用来影响符合某项要求的每个项目,而应该仅用来告诉您集合中的每个项目是否都有影响。如果要将函数应用于谓词评估为true的所有项目,则“好的”方法是

var filtered = array.filter(function(item) {
    return typeof item === "number";
});

var mapped = filtered.map(function(item) {
    return item.foo(); //provided foo() has no side effects and returns a new object of item's type instead.  See note about foreach below.
});

或者,您可以使用foreach而不是地图来就地修改项目。

同样的逻辑适用于some,基本上是:

  • 您用于every测试数组中的所有元素是否都通过了某些测试。
  • 您用于some测试数组中的至少一个元素是否通过某些测试。
  • 您可以map为输入数组中的每个元素返回一个包含1个元素的新数组(这是您选择的函数的结果)。
  • 您使用filter返回长度为0 <阵列length< initial array length元素,所有包含在原始阵列中和所有通过随附的谓词测试。
  • 您可以使用foreach,如果你想地图,就地
  • 您可以使用reduce,如果你想一个数组的结果在一个单一的目标结果(这可能是一个数组,但不必)结合起来。

您使用它们的次数越多(编写LISP代码的次数越多),您就越会意识到它们之间的关系以及如何与他人相互模仿/实现。这些查询的强大功能和真正有趣的是它们的语义,以及它们如何真正推动您消除代码中的有害副作用。

编辑(根据注释):假设您要验证每个元素都是对象,如果它们都有效,则将它们转换为应用程序模型。一次通过的一种方法是:

var dirty = false;
var app_domain_objects = input.map(function(item) {
    if(validate(item)) {
        return new Model(item);
    } else {
        dirty = true; //dirty is captured by the function passed to map, but you know that :)
    }
});
if(dirty) {
    //your validation test failed, do w/e you need to
} else {
    //You can use app_domain_objects
}

这样,当一个对象没有通过验证时,您仍然可以遍历整个数组,这比仅通过进行验证要慢every。但是,在大多数情况下,您的数组都是有效的(或者我希望如此),因此在大多数情况下,您将对数组执行一次传递,最后得到可用的Application Model对象数组。语义将得到尊重,避免副作用,每个人都会感到高兴!

请注意,您也可以编写自己的查询(类似于foreach),该查询将对数组的所有成员应用函数,如果所有成员均通过谓词测试,则返回true / false。就像是:

function apply_to_every(arr, predicate, func) {
    var passed = true;
    for(var i = 0; i < array.length; ++i) {
        if(predicate(arr[i])) {
            func(arr[i]);
        } else {
            passed = false;
            break;
        }
    }
    return passed;
}

虽然那样会修改数组。

我希望这会有所帮助,写起来很有趣。干杯!


感谢您的回答。我并不试图修改的元素代替每本身; 在我的实际代码中,我接收到一个JSON格式的对象数组,因此我首先验证 input if (input.every()),以检查每个元素是否是一个对象(typeof el === "object && el !== null)等,然后,如果验证了,我想将每个元素转换为相应的应用程序模型(其中,现在你提map()我可以使用input.map(function (el) { return new Model(el); });,但不一定到位
艾萨克

..但是请注意,即使map()我不得不遍历两次数组;一次进行验证,另一次进行转换。然而,使用标准的for(;;;)循环,我可以做到这一点使用一个迭代,但我不能找到一个方法来申请everysomemapfilter在这种情况下,并且只能执行一个传球,而不必不希望的副作用的或以其他方式引入bad-实践。
艾萨克(Isaac)

@Isaac Alright,抱歉,抱歉,我现在更清楚地了解您的情况。我将编辑答案以添加一些内容。
pwny

感谢您的出色回答;这真的很有帮助:)。
艾萨克

-1

副作用不在if条件下,而是在if体内。您仅确定是否在实际条件下执行该程序。您的方法在这里没有错。


嗨,谢谢您的回答。抱歉,但是我误解了您的答案,或者您误解了代码……我的代码片段中的所有内容都在if条件之内,并且仅存return在于if的体内;显然,我在说的是“ 想要做的是; ...
Isaac 2013年

1
抱歉,@ Issac的副作用确实在这种if情况下。
罗斯·帕特森
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.