Pratik Yol
"Yanlış" bir çözümün aksine, yalnızca "doğru" ("doğru") ise belirli bir uygulamanın "Doğru Yol ™" olduğunu söylemek yanlış olur. Tomáš'ın çözümü dize tabanlı dizi karşılaştırmasına göre net bir gelişmedir, ancak bu objektif olarak "doğru" anlamına gelmez. Neyse doğru olan nedir ? En hızlı mı? En esnek mi? Anlaması en kolay şey mi? Hata ayıklamak en hızlı mı? En az işlemi kullanıyor mu? Herhangi bir yan etkisi var mı? Hiçbir çözüm her şeyin en iyisini yapamaz.
Tomáš çözümünün hızlı olduğunu söyleyebilirdi ama bunun gereksiz derecede karmaşık olduğunu da söyleyebilirim. Yuvalanmış olsun olmasın tüm diziler için çalışan hepsi bir arada bir çözüm olmaya çalışır. Aslında, girdi olarak sadece dizilerden daha fazlasını kabul eder ve yine de "geçerli" bir cevap vermeye çalışır.
Jenerikler yeniden kullanılabilirlik sunar
Cevabım soruna farklı yaklaşacak. arrayCompare
Sadece dizilerden adım atmakla ilgili genel bir prosedürle başlayacağım . Oradan, gibi diğer temel karşılaştırma fonksiyonları inşa edeceğiz arrayEqual
ve arrayDeepEqual
vb
// arrayCompare :: (a -> a -> Bool) -> [a] -> [a] -> Bool
const arrayCompare = f => ([x,...xs]) => ([y,...ys]) =>
x === undefined && y === undefined
? true
: Boolean (f (x) (y)) && arrayCompare (f) (xs) (ys)
Kanımca, en iyi kod türünün yorumlara bile ihtiyacı yoktur ve bu bir istisna değildir. Burada o kadar az şey var ki, bu prosedürün davranışını neredeyse hiç çaba harcamadan anlayabilirsiniz. Elbette, bazı ES6 sözdizimi size yabancı gelebilir, ancak bunun nedeni yalnızca ES6'nın nispeten yeni olmasıdır.
Türün de belirttiği gibi arrayCompare
, karşılaştırma işlevini f
ve iki giriş dizisini alır xs
ve ys
. Çoğunlukla, tek yaptığımız f (x) (y)
girdi dizilerindeki her eleman için çağırmaktır . false
Kullanıcı tanımlı kısa devre değerlendirmesi sayesinde , kullanıcı tanımlı bir şekilde f
geri gelirse erken dönüyoruz. Yani evet, bu, karşılaştırıcının yinelemeyi erken durdurabileceği ve gereksiz olduğunda giriş dizisinin geri kalanında döngüyü önleyebileceği anlamına gelir.false
&&
Sıkı karşılaştırma
Daha sonra, arrayCompare
fonksiyonumuzu kullanarak , ihtiyaç duyabileceğimiz diğer fonksiyonları kolayca oluşturabiliriz. İlkokulla başlayacağız arrayEqual
…
// equal :: a -> a -> Bool
const equal = x => y =>
x === y // notice: triple equal
// arrayEqual :: [a] -> [a] -> Bool
const arrayEqual =
arrayCompare (equal)
const xs = [1,2,3]
const ys = [1,2,3]
console.log (arrayEqual (xs) (ys)) //=> true
// (1 === 1) && (2 === 2) && (3 === 3) //=> true
const zs = ['1','2','3']
console.log (arrayEqual (xs) (zs)) //=> false
// (1 === '1') //=> false
Bu kadar basit. arrayEqual
ile tanımlanabilir arrayCompare
ile karşılaştıran bir karşılaştırma fonksiyonu ve a
etmek b
kullanılarak ===
(katı eşitliği).
equal
Kendi işlevi olarak da tanımladığımıza dikkat edin . Bu arrayCompare
, birinci dereceden karşılaştırıcımızı başka bir veri türü (Array) bağlamında kullanmak için daha üst düzey bir işlev olarak rolünü vurgular .
Gevşek karşılaştırma
Biz aynı kolaylıkla tanımlanabilir olabilir arrayLooseEqual
bir kullanarak ==
yerine. Şimdi 1
(Number) ile '1'
(String) karşılaştırılırken, sonuç true
…
// looseEqual :: a -> a -> Bool
const looseEqual = x => y =>
x == y // notice: double equal
// arrayLooseEqual :: [a] -> [a] -> Bool
const arrayLooseEqual =
arrayCompare (looseEqual)
const xs = [1,2,3]
const ys = ['1','2','3']
console.log (arrayLooseEqual (xs) (ys)) //=> true
// (1 == '1') && (2 == '2') && (3 == '3') //=> true
Derin karşılaştırma (özyinelemeli)
Muhtemelen bunun sadece sığ bir karşılaştırma olduğunu fark etmişsinizdir. Muhtemelen Tomáš'un çözümü "Doğru Yol ™" dur çünkü derin bir karşılaştırma yapıyor, değil mi?
Eh bizim arrayCompare
prosedür derin eşitlik testi bir esinti kılan bir şekilde kullanmak için yeterli çok yönlü ...
// isArray :: a -> Bool
const isArray =
Array.isArray
// arrayDeepCompare :: (a -> a -> Bool) -> [a] -> [a] -> Bool
const arrayDeepCompare = f =>
arrayCompare (a => b =>
isArray (a) && isArray (b)
? arrayDeepCompare (f) (a) (b)
: f (a) (b))
const xs = [1,[2,[3]]]
const ys = [1,[2,['3']]]
console.log (arrayDeepCompare (equal) (xs) (ys)) //=> false
// (1 === 1) && (2 === 2) && (3 === '3') //=> false
console.log (arrayDeepCompare (looseEqual) (xs) (ys)) //=> true
// (1 == 1) && (2 == 2) && (3 == '3') //=> true
Bu kadar basit. Başka bir üst düzey işlev kullanarak derin bir karşılaştırıcı oluşturuyoruz . Bu sefer tamamlamak üzereyiz arrayCompare
eğer kontrol eden özel bir karşılaştırıcı kullanılarak a
ve b
dizilerdir. Öyleyse, arrayDeepCompare
başka şekilde yeniden karşılaştırın a
ve b
kullanıcı tarafından belirtilen karşılaştırıcıya ( f
) bakın. Bu, derin karşılaştırma davranışını, tek tek öğeleri gerçekte nasıl karşılaştırdığımızdan ayrı tutmamızı sağlar. Yani, gösteriler yukarıdaki örnekte olduğu gibi, derin kullanarak karşılaştırabilirsiniz equal
, looseEqual
ya da başka herhangi karşılaştırıcı yaptığımız.
Çünkü arrayDeepCompare
curried edilir yaptığımız gibi, biz kısmen de önceki örneklerde uygulayabilirsiniz
// arrayDeepEqual :: [a] -> [a] -> Bool
const arrayDeepEqual =
arrayDeepCompare (equal)
// arrayDeepLooseEqual :: [a] -> [a] -> Bool
const arrayDeepLooseEqual =
arrayDeepCompare (looseEqual)
Bana göre Tomas'ın çözümü içinde bu zaten açık bir iyileşme ben çünkü açıkça gerektiği gibi, benim diziler için sığ veya derin karşılaştırmayı seçin.
Nesne karşılaştırması (örnek)
Şimdi bir dizi nesneniz veya başka bir şey varsa? Belki her nesne aynı id
değere sahipse bu dizileri "eşit" olarak değerlendirmek istersiniz ...
// idEqual :: {id: Number} -> {id: Number} -> Bool
const idEqual = x => y =>
x.id !== undefined && x.id === y.id
// arrayIdEqual :: [a] -> [a] -> Bool
const arrayIdEqual =
arrayCompare (idEqual)
const xs = [{id:1}, {id:2}]
const ys = [{id:1}, {id:2}]
console.log (arrayIdEqual (xs) (ys)) //=> true
// (1 === 1) && (2 === 2) //=> true
const zs = [{id:1}, {id:6}]
console.log (arrayIdEqual (xs) (zs)) //=> false
// (1 === 1) && (2 === 6) //=> false
Bu kadar basit. Burada vanilya JS nesneleri kullandım, ancak bu tür karşılaştırıcı herhangi bir nesne türü için işe yarayabilir ; özel nesneleriniz bile. Bu tür bir eşitlik testini desteklemek için Tomáš çözümünün tamamen elden geçirilmesi gerekecektir.
Nesnelerle derin dizi? Problem değil. Çok yönlü, genel işlevler geliştirdik, böylece çok çeşitli kullanım durumlarında çalışacaklar.
const xs = [{id:1}, [{id:2}]]
const ys = [{id:1}, [{id:2}]]
console.log (arrayCompare (idEqual) (xs) (ys)) //=> false
console.log (arrayDeepCompare (idEqual) (xs) (ys)) //=> true
Keyfi karşılaştırma (örnek)
Ya da başka türlü tamamen keyfi bir karşılaştırma yapmak isterseniz? Belki her x
birinin daha büyük olup olmadığını bilmek istiyorum y
...
// gt :: Number -> Number -> Bool
const gt = x => y =>
x > y
// arrayGt :: [a] -> [a] -> Bool
const arrayGt = arrayCompare (gt)
const xs = [5,10,20]
const ys = [2,4,8]
console.log (arrayGt (xs) (ys)) //=> true
// (5 > 2) && (10 > 4) && (20 > 8) //=> true
const zs = [6,12,24]
console.log (arrayGt (xs) (zs)) //=> false
// (5 > 6) //=> false
Az ama öz
Aslında daha az kodla daha fazlasını yaptığımızı görebilirsiniz. arrayCompare
Kendisi hakkında karmaşık bir şey yok ve yaptığımız özel karşılaştırıcıların her birinin çok basit bir uygulaması var.
Kolaylığı ile, iki diziler karşılaştırılabilir dileyebilir tam olarak nasıl tanımlayabilirsiniz - Sığ, derin, sıkı, gevşek, bazı nesne mülkü veya bazı keyfi hesaplama veya bunların herhangi bir kombinasyonu - hepsi tek bir prosedürü kullanarak , arrayCompare
. Belki de bir RegExp
karşılaştırıcı hayal edin ! Çocukların bu normal ifadeleri nasıl sevdiğini biliyorum…
En hızlı mı? Hayır! Ama muhtemelen ikisinin de olması gerekmez. Hız, kodumuzun kalitesini ölçmek için kullanılan tek metrikse, gerçekten harika bir kod çok atılır - Bu yüzden bu yaklaşımı Pratik Yol olarak adlandırıyorum . Ya da belki daha adil olmak, Pratik Bir Yol. Bu açıklama bu cevap için uygundur, çünkü bu cevabın sadece diğer bazı cevaplara kıyasla pratik olduğunu söylemiyorum; nesnel olarak doğrudur. Akıl yürütmesi çok kolay olan çok az kodla yüksek derecede pratiklik elde ettik. Başka hiçbir kod bu açıklamayı kazanamadığımızı söyleyemez.
Bu sizin için "doğru" çözüm mü yapıyor? Karar vermek sizin elinizde . Ve bunu sizin için başka hiç kimse yapamaz; İhtiyaçlarınızın ne olduğunu sadece siz bilirsiniz. Hemen hemen her durumda, akıllı ve hızlı bir şekilde açık, pratik ve çok yönlü bir kod değeri veriyorum. Değer verdiğiniz şey farklı olabilir, bu nedenle sizin için neyin işe yaradığını seçin.
Düzenle
Eski cevabım daha arrayEqual
küçük prosedürlere ayrışmaya odaklanmıştı . Bu ilginç bir alıştırma, ama bu soruna yaklaşmanın en iyi (en pratik) yolu değil. İlgileniyorsanız, bu düzeltme geçmişini görebilirsiniz.