व्यावहारिक तरीका है
मुझे लगता है कि यह कहना गलत है कि एक विशेष कार्यान्वयन "सही तरीका ™" है यदि यह "गलत" समाधान के विपरीत केवल "सही" ("सही") है। टॉम का समाधान स्ट्रिंग-आधारित सरणी तुलना पर एक स्पष्ट सुधार है, लेकिन इसका मतलब यह नहीं है कि इसका उद्देश्य "सही" है। वैसे भी सही क्या है ? क्या यह सबसे तेज है? क्या यह सबसे अधिक लचीला है? क्या इसे समझना सबसे आसान है? क्या यह डिबग करने के लिए सबसे तेज है? क्या यह कम से कम संचालन का उपयोग करता है? क्या इसका कोई साइड इफेक्ट है? किसी भी समाधान से सभी चीजों का सर्वश्रेष्ठ नहीं हो सकता है।
Tomáš कह सकता है कि उसका समाधान तेज़ है लेकिन मैं यह भी कहूंगा कि यह अनावश्यक रूप से जटिल है। यह एक ऑल-इन-वन समाधान बनने की कोशिश करता है जो सभी एरेज़, नेस्टेड या नहीं के लिए काम करता है। वास्तव में, यह इनपुट के रूप में केवल सरणियों से अधिक स्वीकार करता है और फिर भी "वैध" उत्तर देने का प्रयास करता है।
जेनरिक पुन: प्रयोज्यता प्रदान करते हैं
मेरे जवाब से समस्या अलग तरीके से सामने आएगी। मैं एक सामान्य arrayCompare
प्रक्रिया से शुरू करूंगा जो केवल सरणियों के माध्यम से कदम रखने से संबंधित है। वहां से, हम अपने अन्य बुनियादी तुलना कार्यों जैसे arrayEqual
और arrayDeepEqual
, आदि का निर्माण करेंगे
// 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)
मेरी राय में, सर्वोत्तम प्रकार के कोड को टिप्पणियों की भी आवश्यकता नहीं है, और यह कोई अपवाद नहीं है। यहां ऐसा बहुत कम हो रहा है कि आप इस प्रक्रिया के व्यवहार को लगभग बिना किसी प्रयास के समझ सकते हैं। निश्चित रूप से, ES6 के कुछ वाक्य-विन्यास आपको अभी विदेशी लग सकते हैं, लेकिन ऐसा केवल इसलिए है क्योंकि ES6 अपेक्षाकृत नया है।
जैसा कि प्रकार से पता चलता है, arrayCompare
तुलना फ़ंक्शन f
और दो इनपुट सरणियों को लेता है , xs
और ys
। अधिकांश भाग के लिए, हम जो भी करते हैं f (x) (y)
, वह इनपुट एरे में प्रत्येक तत्व के लिए होता है। false
यदि उपयोगकर्ता द्वारा परिभाषित f
रिटर्न false
- हम &&
शॉर्ट-सर्किट मूल्यांकन के लिए धन्यवाद देते हैं , तो हम जल्दी लौटते हैं । तो हां, इसका मतलब है कि तुलनित्र जल्दी से चलना बंद कर सकता है और अनावश्यक होने पर बाकी इनपुट सरणी के माध्यम से लूपिंग को रोक सकता है।
सख्त तुलना
अगला, हमारे arrayCompare
फ़ंक्शन का उपयोग करके , हम आसानी से अन्य कार्यों को बना सकते हैं जिनकी हमें आवश्यकता हो सकती है। हम प्राथमिक के साथ शुरू करेंगे 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
इतना ही आसान। arrayEqual
के साथ परिभाषित किया जा सकता है arrayCompare
और एक तुलनित्र फ़ंक्शन a
का b
उपयोग करने के लिए तुलना करता है ===
(सख्त समानता के लिए)।
ध्यान दें कि हम equal
इसे स्वयं के कार्य के रूप में भी परिभाषित करते हैं। यह arrayCompare
दूसरे डेटा प्रकार (एरे) के संदर्भ में हमारे पहले ऑर्डर तुलनित्र का उपयोग करने के लिए एक उच्च-क्रम फ़ंक्शन के रूप में भूमिका पर प्रकाश डालता है ।
तुलना की ढीली
हम इसके बजाय आसानी से परिभाषित arrayLooseEqual
कर सकते हैं ==
। अब 1
(संख्या) की तुलना ( '1'
स्ट्रिंग) करने पर, परिणाम होगा 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
गहरी तुलना (पुनरावर्ती)
आपने शायद गौर किया है कि यह केवल उथले तुलना थो है। निश्चित रूप से टॉम का समाधान "द राइट वे ™" है, क्योंकि इसमें निहित तुलना गहरी है, है ना?
अच्छी तरह से हमारी arrayCompare
प्रक्रिया एक तरह से उपयोग करने के लिए पर्याप्त है जो एक गहरी समानता का परीक्षण एक हवा बनाता है ...
// 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
इतना ही आसान। हम एक अन्य उच्च-क्रम फ़ंक्शन का उपयोग करके एक गहरे तुलनित्र का निर्माण करते हैं। इस बार हम arrayCompare
एक कस्टम तुलनित्र का उपयोग कर रैपिंग कर रहे हैं जो यह जाँच करेगा कि क्या हैं a
और b
एरेज़ हैं। यदि हां, तो arrayDeepCompare
फिर से अन्यथा तुलना करें a
और b
उपयोगकर्ता द्वारा निर्दिष्ट तुलनित्र ( f
) के लिए। यह हमें गहरे तुलना व्यवहार को अलग रखने की अनुमति देता है कि हम वास्तव में व्यक्तिगत तत्वों की तुलना कैसे करते हैं। यानी, शो ऊपर के उदाहरण की तरह, हम गहरा का उपयोग कर की तुलना कर सकते equal
, looseEqual
या किसी भी अन्य तुलनित्र हम बनाते हैं।
क्योंकि arrayDeepCompare
करी है, हम इसे आंशिक रूप से लागू कर सकते हैं जैसे हमने पिछले उदाहरणों में भी किया था
// arrayDeepEqual :: [a] -> [a] -> Bool
const arrayDeepEqual =
arrayDeepCompare (equal)
// arrayDeepLooseEqual :: [a] -> [a] -> Bool
const arrayDeepLooseEqual =
arrayDeepCompare (looseEqual)
मेरे लिए, यह पहले से ही टॉम के समाधान पर एक स्पष्ट सुधार है क्योंकि मैं स्पष्ट रूप से अपने सरणियों के लिए उथले या गहरी तुलना का चयन कर सकता हूं, आवश्यकतानुसार।
वस्तु तुलना (उदाहरण)
अब अगर आपके पास वस्तुओं या कुछ और की एक सरणी है तो क्या होगा? हो सकता है कि आप उन सरणियों को "बराबर" मानना चाहें, यदि प्रत्येक वस्तु का समान id
मूल्य हो ...
// 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
इतना ही आसान। यहां मैंने वेनिला जेएस वस्तुओं का उपयोग किया है, लेकिन इस प्रकार का तुलनित्र किसी भी वस्तु प्रकार के लिए काम कर सकता है ; यहां तक कि अपने कस्टम वस्तुओं। इस तरह के समानता परीक्षण का समर्थन करने के लिए टॉम के समाधान को पूरी तरह से फिर से तैयार करने की आवश्यकता होगी
वस्तुओं के साथ गहरी सरणी? एक समस्या नहीं है। हमने अत्यधिक बहुमुखी, सामान्य कार्य बनाए हैं, इसलिए वे विभिन्न प्रकार के उपयोग मामलों में काम करेंगे।
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
तुलनात्मक तुलना (उदाहरण)
या क्या होगा यदि आप किसी अन्य प्रकार की पूरी तरह से मनमानी तुलना करना चाहते थे? शायद मैं जानना चाहता हूं कि क्या प्रत्येक एक x
से अधिक है 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
थोड़ा ही काफी है
आप देख सकते हैं कि हम वास्तव में कम कोड के साथ अधिक कर रहे हैं। हमारे बारे में कुछ भी जटिल नहीं है arrayCompare
और हमने जो भी कस्टम कम्पैक्टर बनाए हैं उनमें से प्रत्येक का बहुत ही सरल कार्यान्वयन है।
आसानी के साथ, हम परिभाषित कर सकते हैं वास्तव में दो सरणियों तुलना करने के लिए के लिए हम कैसे इच्छा - उथले, गहरी, सख्त, ढीले, किसी वस्तु संपत्ति, या कुछ मनमाने ढंग से गणना, या इनमें से किसी भी संयोजन - सब एक प्रक्रिया का उपयोग कर , arrayCompare
। शायद एक RegExp
तुलनित्र भी सपना ! मुझे पता है कि बच्चे उन रेगीक्स से कैसे प्यार करते हैं ...
क्या यह सबसे तेज है? नहीं। लेकिन यह शायद होने की जरूरत नहीं है। यदि गति हमारे कोड की गुणवत्ता को मापने के लिए उपयोग की जाने वाली एकमात्र मीट्रिक है, तो वास्तव में बहुत सारे महान कोड को फेंक दिया जाएगा - यही कारण है कि मैं इस दृष्टिकोण को प्रेक्टिकल वे कह रहा हूं । या शायद अधिक निष्पक्ष होने के लिए, ए प्रैक्टिकल वे। यह विवरण इस उत्तर के लिए उपयुक्त है क्योंकि मैं यह नहीं कह रहा हूँ कि यह उत्तर कुछ अन्य उत्तर की तुलना में केवल व्यावहारिक है; यह वास्तव में सच है। हम बहुत कम कोड के साथ व्यावहारिकता का एक उच्च स्तर प्राप्त कर चुके हैं, जिसके बारे में तर्क करना बहुत आसान है। कोई अन्य कोड यह नहीं कह सकता कि हमने यह विवरण अर्जित नहीं किया है।
क्या यह आपके लिए "सही" समाधान बनाता है? यह तय करने के लिए आप पर निर्भर है। और कोई भी आपके लिए ऐसा नहीं कर सकता है; केवल आप ही जानते हैं कि आपकी जरूरतें क्या हैं। लगभग सभी मामलों में, मैं चालाक और तेज तरह से सीधे, व्यावहारिक और बहुमुखी कोड को महत्व देता हूं। आपके लिए अलग मूल्य हो सकते हैं, इसलिए आपके लिए क्या काम करता है।
संपादित करें
मेरा पुराना उत्तर arrayEqual
छोटे प्रक्रियाओं में विघटित होने पर अधिक केंद्रित था । यह एक दिलचस्प अभ्यास है, लेकिन वास्तव में इस समस्या से निपटने का सबसे अच्छा (सबसे व्यावहारिक) तरीका नहीं है। यदि आप रुचि रखते हैं, तो आप इस संशोधन इतिहास को देख सकते हैं।