ผลข้างเคียงใน“ ทุกคน” ของ 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) someและevery( ฉันควรใช้สิ่งfor(;;;)นี้หรือไม่)
  3. มีแนวทางที่ดีกว่านี้ไหม?

3
ทุกคนรวมถึงตัวกรองแผนที่และการลดขนาดเป็นการสืบค้นพวกเขาไม่มีผลข้างเคียงหากพวกเขาทำผิดคุณ
Benjamin Gruenbaum

@BenjaminGruenbaum: นั่นไม่ได้ทำให้พวกเขาฟันบ่อยกว่าหรือไม่? 9/10 ถ้าฉันใช้someฉันต้องการทำบางสิ่งกับองค์ประกอบถ้าฉันใช้everyฉันต้องการทำบางสิ่งกับองค์ประกอบทั้งหมดเหล่านั้น ... someและeveryอย่าให้ฉันเข้าถึงข้อมูลนั้นดังนั้นฉันจึงไม่สามารถ ใช้พวกเขาหรือฉันต้องเพิ่มผลข้างเคียง
Isaac

ไม่เมื่อฉันอ้างถึงผลข้างเคียงฉันหมายถึงภายในหัวของร่างกายถ้าไม่ใช่ ภายในร่างกายคุณสามารถปรับเปลี่ยนได้ตามที่คุณต้องการ ไม่ต้องทำให้กลายพันธุ์วัตถุภายในโทรกลับที่คุณส่งไปบาง / เมื่อ
Benjamin Gruenbaum

@BenjaminGruenbaum: แต่นั่นคือจุดของฉัน ถ้าฉันใช้someในifเงื่อนไขของฉันเพื่อตรวจสอบว่าองค์ประกอบบางอย่างในอาร์เรย์แสดงคุณสมบัติบางอย่าง 9/10 ฉันจำเป็นต้องทำงานกับองค์ประกอบนั้นในifร่างกายของฉัน; ตอนนี้เป็นsomeไม่ได้บอกฉันซึ่งองค์ประกอบการจัดแสดงทรัพย์สิน (เพียงแค่ "ใครทำ") ผมสามารถใช้some อีกครั้งในร่างกาย (O (2n)) หรือฉันก็สามารถดำเนินการได้ภายในถ้าสภาพ ( ซึ่งเป็นสิ่งที่ไม่ดีเพราะมันเป็นผลข้างเคียงที่อยู่ภายในหัว)
Isaac

... เช่นเดียวกันกับeveryเช่นกันแน่นอน
Isaac

คำตอบ:


9

ถ้าผมเข้าใจจุดของคุณอย่างถูกต้องคุณดูเหมือนจะผิดพลาดการใช้หรือการเหยียดหยามeveryและsomeแต่ก็หลีกเลี่ยงไม่ได้เล็ก ๆ น้อย ๆ ถ้าคุณต้องการที่จะเปลี่ยนองค์ประกอบของอาร์เรย์ของคุณโดยตรง แก้ไขให้ถูกต้องหากฉันผิด แต่สิ่งที่คุณพยายามทำคือตรวจสอบว่าองค์ประกอบบางส่วนหรือทุกส่วนในลำดับของคุณมีเงื่อนไขที่แน่นอนแล้วแก้ไของค์ประกอบเหล่านั้น นอกจากนี้โค้ดของคุณดูเหมือนจะใช้บางอย่างกับทุกรายการจนกว่าคุณจะพบรหัสที่ไม่ผ่านเพรดิเคตและฉันไม่คิดว่านั่นคือสิ่งที่คุณต้องการจะทำ อย่างไรก็ตาม.

ลองมาตัวอย่างแรกของคุณ (แก้ไขเล็กน้อย)

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

สิ่งที่คุณกำลังทำอยู่ที่นี่จริง ๆ แล้วขัดแย้งกับจิตวิญญาณของแนวคิด / / แผนที่ / ลด / กรอง / ฯลฯ Everyไม่ได้ใช้เพื่อส่งผลกระทบต่อทุกรายการที่สอดคล้องกับบางอย่าง แต่ควรใช้เพื่อบอกคุณว่าทุกรายการในคอลเลกชันทำหรือไม่ หากคุณต้องการที่จะใช้ฟังก์ชั่นกับทุกรายการที่เพรดิเคตประเมินเป็นจริงวิธีที่ดีที่จะทำคือ

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
}

วิธีนี้เมื่อวัตถุไม่ได้ผ่านการตรวจสอบคุณยังคงเก็บ iterating everyผ่านอาร์เรย์ทั้งหมดซึ่งจะช้ากว่าเพียงแค่การตรวจสอบด้วย อย่างไรก็ตามเวลาส่วนใหญ่ของคุณจะถูกต้อง (หรือฉันหวังว่าจะเป็นเช่นนั้น) ดังนั้นในกรณีส่วนใหญ่คุณจะดำเนินการส่งผ่านหนึ่งครั้งผ่านอาร์เรย์ของคุณ ความหมายจะได้รับการเคารพหลีกเลี่ยงผลข้างเคียงและทุกคนจะมีความสุข!

โปรดทราบว่าคุณสามารถเขียนคิวรีของคุณเองคล้ายกับ foreach ซึ่งจะใช้ฟังก์ชันกับสมาชิกทั้งหมดของอาเรย์และส่งกลับค่าจริง / เท็จหากพวกเขาทั้งหมดผ่านการทดสอบภาคแสดง สิ่งที่ต้องการ:

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

แม้ว่ามันจะแก้ไขอาเรย์

ฉันหวังว่านี่จะช่วยได้มันสนุกมากที่จะเขียน ไชโย!


ขอบคุณสำหรับคำตอบ. ฉันไม่จำเป็นต้องพยายามที่จะปรับเปลี่ยนองค์ประกอบในสถานที่ต่อ SE; ในรหัสจริงของฉันฉันได้รับอาร์เรย์ที่จัดรูปแบบของ JSON ดังนั้นฉันจึงตรวจสอบอินพุตif (input.every())ก่อนเพื่อตรวจสอบว่าแต่ละองค์ประกอบเป็นวัตถุ ( typeof el === "object && el !== null) ฯลฯจากนั้นถ้าตรวจสอบได้ฉันต้องการแปลงแต่ละองค์ประกอบเป็น ตามลำดับการประยุกต์ใช้แบบจำลอง (ซึ่งตอนนี้คุณพูดถึงmap()ฉันสามารถใช้input.map(function (el) { return new Model(el); });แต่ไม่จำเป็นต้องอยู่ในสถานที่ .
ไอแซก

.. แต่ดูสิว่าถึงแม้map()ฉันจะต้องทำซ้ำสองครั้ง หนึ่งครั้งเพื่อตรวจสอบและอีกรายการหนึ่งสำหรับแปลง อย่างไรก็ตามการใช้มาตรฐานfor(;;;)ห่วงฉันจะทำเช่นนี้ใช้หนึ่งซ้ำ แต่ฉันไม่สามารถหาวิธีที่จะใช้every, some, mapหรือfilterในสถานการณ์นี้และดำเนินการเพียงหนึ่งผ่านโดยไม่ต้องพึงประสงค์-ผลข้างเคียงหรือมิฉะนั้นแนะนำ bad- การปฏิบัติ
Isaac

@Isaac เอาล่ะขอโทษสำหรับความล่าช้าฉันเข้าใจสถานการณ์ของคุณชัดเจนขึ้นแล้ว ฉันจะแก้ไขคำตอบเพื่อเพิ่มบางสิ่ง
pwny

ขอบคุณสำหรับคำตอบที่ดี มันเป็นประโยชน์จริงๆ :)
Isaac

-1

ผลข้างเคียงที่ไม่ได้อยู่ในเงื่อนไขถ้าพวกเขาอยู่ในร่างกายของถ้า คุณได้พิจารณาแล้วว่าจะดำเนินการร่างกายในสภาพที่เป็นจริงหรือไม่ ไม่มีอะไรผิดปกติกับวิธีการของคุณที่นี่


สวัสดีขอบคุณสำหรับคำตอบของคุณ ขออภัย แต่ฉันเข้าใจผิดคำตอบของคุณหรือคุณตีความรหัสผิดไป ... ทุกอย่างในโค้ดของฉันอยู่ในifเงื่อนไขโดยมี แต่สิ่งที่returnอยู่ภายในifร่างกายของเท่านั้น เห็นได้ชัดว่าผมกำลังพูดถึงตัวอย่างรหัสนำหน้าด้วย " สิ่งที่ต้องการจะทำคือ; ...
ไอแซก

1
ขออภัยผลข้างเคียงของ @ Issac อยู่ในifสภาพที่แน่นอน
Ross Patterson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.