Array'ın “her” veya “bazı” larının yan etkileri kötü mü?


9

Her zaman bir ifdurumda yan etkilere sahip olmanın kötü olduğu öğretildi . Demek istediğim ... dir;

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

... aksine;

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

... ve bunu anlıyorum ve meslektaşlarım mutlu çünkü bunu yapmıyorum ve hepimiz Cuma günü saat 17: 00'de eve gidiyoruz ve herkes mutlu bir hafta sonu geçiriyor.

Şimdi, ECMAScript5'i gibi yöntemler tanıtıldı every()ve some()hiç Array, ben onları çok yararlı buluyorum. Onlar daha temiz for (;;;), size başka bir kapsam vermek ve bir değişken tarafından eleman erişilebilir hale getirmek.

Ancak girdiyi doğrularken, girdiyi doğrulamak için every/ somekoşulunu kullanarak kendimi daha sık bulamıyorum , daha sonra girdiyi kullanılabilir bir modele dönüştürmek için gövdede every/ öğesini some tekrar kullanıyorum ;

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

... yapmak istediğim şey;

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

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

    return res;
})) {
    return;
}

... bana ifkötü bir durumda yan etkiler veriyor .

Buna karşılık, kod eski ile bakmak olurdu for (;;;);

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

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

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

Sorularım:

  1. Bu kesinlikle kötü bir uygulama mı?
  2. Am I (mis | ab) kullanılarak someve every( gereken bir kullanıyor for(;;;)bunun için?)
  3. Daha iyi bir yaklaşım var mı?

3
Filtreleme, haritalama ve azaltmanın yanı sıra her biri sorgulardır, yan etkileri yoktur, eğer yaparlarsa onları kötüye kullanırsınız.
Benjamin Gruenbaum

@BenjaminGruenbaum: Öyleyse bu onları sık sık dişsiz yapmıyor mu? 9/10, eğer kullanırsam some, elemanla bir şey yapmak istiyorum , kullanırsam every, tüm bu elemanlara bir şey yapmak istiyorum ... someve everybu bilgilere erişmeme izin vermiyorum, bu yüzden ya yapamam onları kullanın, ya da yan etkiler eklemek zorundayım.
Isaac

Hayýr. Yan etkilerden bahsettiđimde, vücudun olmasa da kafanýn içinde. Vücudun içinde istediğiniz gibi değiştirebilirsiniz. Sadece / ne zaman geçtiğiniz geri arama içindeki nesneyi değiştirmeyin.
Benjamin Gruenbaum

@BenjaminGruenbaum: Ama bu tam olarak benim açımdan. someBenim durumumda ifdizideki belirli bir öğenin belirli bir özellik gösterip göstermediğini belirlemek için kullanırsam , 9/10 Vücudumdaki o öğe üzerinde çalışmam gerekiyor if; şimdi, somebana hangi elemanların özelliği sergilediğini söylemediğinden (sadece "biri yaptı"), ya some tekrar vücutta kullanabilirim (O (2n)), ya da sadece if koşulu içindeki işlemi yapabilirim ( ki bu kötü, çünkü kafa içinde bir yan etkisi var).
Isaac

... aynı şey everyelbette geçerlidir .
Isaac

Yanıtlar:


9

Eğer fikrinizi doğru anlarsam, yanlış kullanıyorsunuz veya kötüye kullanıyorsunuzdur everyve somedizilerinizin elemanlarını doğrudan değiştirmek istiyorsanız bu kaçınılmazdır. Yanılıyorsam beni düzeltin, ancak yapmaya çalıştığınız şey, dizinizdeki bazı öğelerin veya her bir öğenin belirli bir durum gösterip göstermediğini bulmaktır. Ayrıca, kod yüklemi geçmeyen bir tane bulana kadar tüm öğelere bir şeyler uyguluyor gibi görünüyor ve ne yapmak istediğinizi sanmıyorum. Neyse.

İlk örneğinizi ele alalım (biraz değiştirildi)

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

Burada yaptığınız şey, bazı / every / map / reduce / filter / etc kavramlarının ruhuna karşı biraz gider. Everybir şeye uyan her öğeyi etkilemek için kullanılmaz, yalnızca bir koleksiyondaki her öğenin yapılıp yapılmadığını size bildirmek için kullanılmalıdır. Bir yüklemin doğru olarak değerlendirildiği tüm öğelere bir işlev uygulamak istiyorsanız, bunu yapmanın "iyi" yolu

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

Alternatif olarak, foreachöğeleri yerinde değiştirmek için harita yerine kullanabilirsiniz .

Aynı mantık sometemel olarak:

  • Sen kullanmak everydizideki tüm öğeler bazı testini geçerse testine.
  • Kullanabilirsiniz somebir dizideki en az bir elemanı, testi geçerse test.
  • mapBir girdi dizisindeki her öğe için 1 öğe içeren (seçtiğiniz bir işlevin sonucu olan) yeni bir dizi döndürmek için kullanılır .
  • Kullanabilirsiniz filteruzunluğu 0 <bir dizi geri length< initial array lengthelemanları, verilen yüklem testi geçen özgün dizi içerdiği ve tüm kadar.
  • Sen kullanmak foreacheşlemek ama yerinde isterseniz
  • Sen kullanmak reduce(bir dizi olabilir ama olmak zorunda değildir) tek bir nesne sonucu bir dizi sonuçları birleştirmek istiyorum.

Onları ne kadar çok kullanırsanız (ve LISP kodunu o kadar çok yazarsanız), bunların nasıl ilişkili olduğunu ve birisini diğerleriyle taklit etmenin / uygulamanın nasıl mümkün olduğunu daha fazla fark edersiniz. Bu sorgularda güçlü olan ve gerçekten ilginç olan şey, semantikleri ve sizi kodunuzdaki zararlı yan etkileri ortadan kaldırmaya nasıl ittikleri.

DÜZENLE (yorumlar ışığında): Diyelim ki her öğenin bir nesne olduğunu doğrulamak ve hepsi geçerliyse bunları bir Uygulama Modeline dönüştürmek istiyorsunuz. Bunu tek bir geçişte yapmanın bir yolu:

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
}

Bu şekilde, bir nesne doğrulamayı geçmediğinde, yineleme işlemini yalnızca doğrulamaktan daha yavaş olan tüm dizi boyunca sürdürmeye devam edersiniz every. Ancak, çoğu zaman diziniz geçerli olacaktır (ya da ben bunu ummalıyım), bu nedenle çoğu durumda diziniz üzerinden tek bir geçiş gerçekleştirir ve kullanılabilir bir Uygulama Modeli nesneleri dizisi ile sonuçlanırsınız. Anlambilime saygı gösterilecek, yan etkilerden kaçınılacak ve herkes mutlu olacak!

Ayrıca, dizinin tüm üyelerine bir işlev uygulayacak ve hepsi bir yüklem testi geçerse true / false döndüren foreach benzeri kendi sorgunuzu yazabileceğinizi unutmayın. Gibi bir şey:

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

Rağmen bu yerinde dizi değiştirmek.

Umarım bu yardımcı olur, yazmak çok eğlenceliydi. Şerefe!


Cevabınız için teşekkürler. Ben mutlaka per-se yerine öğeleri değiştirmek için çalışmıyorum ; benim gerçek kod, bir ilk ediyorum bu yüzden, bir nesne JSON biçimli bir dizi alıcı ediyorum doğrulama girişi if (input.every()), her bir eleman olduğunu kontrol etmek için olan (bir amacı typeof el === "object && el !== null, vs) sonra doğrular, ben her bir öğe dönüştürmek istiyorum ilgili Uygulama Modeli (ki şimdi map()kullanabileceğimi input.map(function (el) { return new Model(el); });söylüyorsunuz; ancak zorunlu olarak yerinde değil .)
Isaac

.. ama görüyorum ki map()ben bile dizi üzerinde iki kez yinelemek zorunda; bir kez doğrulamak için ve bir diğeri dönüştürmek için. Bununla birlikte, standart kullanılarak for(;;;)döngü, bunu kullanarak tek yineleme yapabilirdi ama uygulamak için bir yol bulamıyorum every, some, mapveya filtersadece bu senaryoda, ve gerçekleştirmek biri istenmeyen-yan etkileri bulunduğu veya başka kötü-tanıtma olmadan, pas uygulama.
Isaac

@Isaac Tamam, gecikme için özür dilerim, şimdi durumunuzu daha net anlıyorum. Bazı şeyler eklemek için cevabımı düzenleyeceğim.
pwny

Harika cevap için teşekkürler; gerçekten yardımcı oldu :).
Isaac

-1

Yan etkiler if durumunda değil, if'in vücudundadır. Yalnızca bu gövdeyi fiili durumda yürütüp yürütmeyeceğinizi belirlediniz. Burada yaklaşımınızla ilgili yanlış bir şey yok.


Merhaba, Cevabınız için teşekkürler. Maalesef ama her iki Cevabını yanlış anladın veya kod yanlış yorumlandığını ettik ... Benim kod parçacığı her şey içinde if, yalnızca sona durumun returniç varlık ifgövdesine s'; Açıkçası ben " ne yapmak istiyorum olduğunu; ...
Isaac

1
Üzgünüz, @ Issac'ın yan etkileri gerçekten de ifdurumdaydı.
Ross Patterson
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.