कार्य (ECMAScript)
आपको केवल फ़ंक्शन परिभाषा और फ़ंक्शन कॉल की आवश्यकता है। आप किसी भी शाखाओं, सशर्त, ऑपरेटरों या बिलिन कार्यों की आवश्यकता नहीं है। मैं ECMAScript का उपयोग करके एक कार्यान्वयन प्रदर्शित करूंगा।
सबसे पहले, चलो नामक दो कार्यों को परिभाषित करते हैं true
और false
। हम उन्हें किसी भी तरह से परिभाषित कर सकते हैं जो हम चाहते हैं, वे पूरी तरह से मनमाना हैं, लेकिन हम उन्हें बहुत ही विशेष तरीके से परिभाषित करेंगे जिसके कुछ फायदे हैं जैसा कि हम बाद में देखेंगे:
const tru = (thn, _ ) => thn,
fls = (_ , els) => els;
tru
एक ऐसा कार्य है जो दो मापदंडों के साथ होता है जो केवल इसके दूसरे तर्क को अनदेखा करता है और पहला लौटाता है। fls
दो मापदंडों के साथ एक फ़ंक्शन भी है जो केवल इसके पहले तर्क को अनदेखा करता है और दूसरे को लौटाता है।
हमने क्यों tru
और fls
इस तरह से एनकोड किया ? खैर, इस तरह से, दो कार्य केवल के दो अवधारणाओं का प्रतिनिधित्व नहीं true
और false
, नहीं, एक ही समय में, वे भी "विकल्प" की अवधारणा का प्रतिनिधित्व करते हैं, दूसरे शब्दों में, वे भी एक हैं if
/ then
/ else
अभिव्यक्ति! हम if
स्थिति का मूल्यांकन करते हैं और इसे तर्क के रूप में then
ब्लॉक और ब्लॉक पास करते हैं else
। यदि स्थिति का मूल्यांकन tru
करता then
है, तो यह ब्लॉक को लौटा देगा , यदि यह मूल्यांकन करता है, तो यह ब्लॉक fls
को वापस कर देगा else
। यहाँ एक उदाहरण है:
tru(23, 42);
// => 23
यह रिटर्न 23
, और यह:
fls(23, 42);
// => 42
42
जैसा आप उम्मीद करेंगे, वैसा ही रिटर्न ।
एक शिकन है, हालांकि:
tru(console.log("then branch"), console.log("else branch"));
// then branch
// else branch
यह दोनों प्रिंट then branch
और else branch
! क्यूं कर?
ठीक है, यह पहले तर्क का रिटर्न मूल्य देता है, लेकिन यह दोनों तर्कों का मूल्यांकन करता है, क्योंकि ECMAScript सख्त है और फ़ंक्शन को कॉल करने से पहले एक फ़ंक्शन के सभी तर्कों का हमेशा मूल्यांकन करता है। IOW: यह पहले तर्क का मूल्यांकन करता है console.log("then branch")
, जो कि केवल रिटर्न करता है undefined
और then branch
कंसोल पर प्रिंटिंग का साइड-इफेक्ट होता है , और यह दूसरे तर्क का मूल्यांकन करता है, जो undefined
कंसोल पर साइड-इफेक्ट के रूप में रिटर्न और प्रिंट भी करता है। फिर, यह पहला रिटर्न देता है undefined
।
Λ-पथरी में, जहां इस एन्कोडिंग का आविष्कार किया गया था, यह कोई समस्या नहीं है: λ-पथरी शुद्ध है , जिसका अर्थ है कि इसका कोई दुष्प्रभाव नहीं है; इसलिए आप कभी भी नोटिस नहीं करेंगे कि दूसरे तर्क का भी मूल्यांकन किया जाता है। इसके अलावा, λ-पथरी आलसी है (या कम से कम, यह अक्सर सामान्य आदेश के तहत मूल्यांकन किया जाता है), जिसका अर्थ है, यह वास्तव में उन तर्कों का मूल्यांकन नहीं करता है जिनकी आवश्यकता नहीं है। तो, IOW: λ-पथरी में दूसरे तर्क का मूल्यांकन कभी नहीं किया जाएगा, और यदि यह होता तो हम नोटिस नहीं करते।
हालाँकि, ECMAScript सख्त है , अर्थात यह हमेशा सभी तर्कों का मूल्यांकन करता है। ठीक है, वास्तव में, हमेशा नहीं: if
/ then
/ else
, उदाहरण के लिए, केवल then
शाखा का मूल्यांकन करता है यदि स्थिति है true
और केवल else
शाखा का मूल्यांकन करता है यदि स्थिति है false
। और हम इस व्यवहार को अपने साथ दोहराना चाहते हैं iff
। शुक्र है, भले ही ECMAScript आलसी न हो, लेकिन कोड के टुकड़े के मूल्यांकन में देरी करने का एक तरीका है, उसी तरह लगभग हर दूसरी भाषा करती है: इसे किसी फ़ंक्शन में लपेटें, और यदि आप उस फ़ंक्शन को कभी नहीं कहते हैं, तो कोड होगा कभी अमल नहीं किया।
इसलिए, हम एक फ़ंक्शन में दोनों ब्लॉक लपेटते हैं, और अंत में दिए गए फ़ंक्शन को कॉल करते हैं:
tru(() => console.log("then branch"), () => console.log("else branch"))();
// then branch
प्रिंट then branch
और
fls(() => console.log("then branch"), () => console.log("else branch"))();
// else branch
प्रिंट करता है else branch
।
हम पारंपरिक if
/ then
/ else
इस तरह से लागू कर सकते हैं :
const iff = (cnd, thn, els) => cnd(thn, els);
iff(tru, 23, 42);
// => 23
iff(fls, 23, 42);
// => 42
फिर से, हमें फ़ंक्शन को कॉल करते समय कुछ अतिरिक्त फ़ंक्शन रैपिंग की आवश्यकता iff
होती है और अतिरिक्त फ़ंक्शन कॉल को परिभाषा में कोष्ठक कहते हैं iff
, उसी कारण से:
const iff = (cnd, thn, els) => cnd(thn, els)();
iff(tru, () => console.log("then branch"), () => console.log("else branch"));
// then branch
iff(fls, () => console.log("then branch"), () => console.log("else branch"));
// else branch
अब जबकि हमारे पास वे दो परिभाषाएँ हैं, हम लागू कर सकते हैं or
। सबसे पहले, हम इसके लिए सत्य सारणी को देखते हैं or
: यदि पहला संकार्य सत्य है, तो अभिव्यक्ति का परिणाम पहले कैंडिड के समान है। अन्यथा, अभिव्यक्ति का परिणाम दूसरे ऑपरेंड का परिणाम है। संक्षेप में: यदि पहला ऑपरेंड है true
, तो हम पहला ऑपरेंड वापस करते हैं, अन्यथा हम दूसरा ऑपरेंड वापस करते हैं:
const orr = (a, b) => iff(a, () => a, () => b);
आइए देखें कि यह काम करता है:
orr(tru,tru);
// => tru(thn, _) {}
orr(tru,fls);
// => tru(thn, _) {}
orr(fls,tru);
// => tru(thn, _) {}
orr(fls,fls);
// => fls(_, els) {}
महान! हालाँकि, वह परिभाषा थोड़ी बदसूरत लग रही है। याद रखें, tru
और fls
पहले से ही अपने आप से एक सशर्त की तरह कार्य करते हैं, इसलिए वास्तव में इसकी कोई आवश्यकता नहीं है iff
, और इस प्रकार सभी उस फ़ंक्शन को सभी पर लपेटते हैं:
const orr = (a, b) => a(a, b);
आपके पास यह है: or
(और अन्य बूलियन ऑपरेटर) केवल कुछ ही पंक्तियों में फ़ंक्शन परिभाषाओं और फ़ंक्शन कॉल के साथ कुछ भी परिभाषित नहीं करते हैं:
const tru = (thn, _ ) => thn,
fls = (_ , els) => els,
orr = (a , b ) => a(a, b),
nnd = (a , b ) => a(b, a),
ntt = a => a(fls, tru),
xor = (a , b ) => a(ntt(b), b),
iff = (cnd, thn, els) => cnd(thn, els)();
दुर्भाग्य से, यह कार्यान्वयन बेकार है: ECMAScript में कोई फ़ंक्शंस या ऑपरेटर नहीं हैं जो वापस आते हैं tru
या fls
, वे सभी वापस लौटते हैं true
या false
, इसलिए हम उन्हें अपने कार्यों के साथ उपयोग नहीं कर सकते हैं। लेकिन अभी भी बहुत कुछ है जो हम कर सकते हैं। उदाहरण के लिए, यह एक एकल-लिंक्ड सूची का कार्यान्वयन है:
const cons = (hd, tl) => which => which(hd, tl),
car = l => l(tru),
cdr = l => l(fls);
ऑब्जेक्ट्स (स्काला)
आपने कुछ अजीबोगरीब देखा होगा: tru
और fls
दोहरी भूमिका निभाते हुए, वे डेटा मानों के रूप में कार्य करते हैं true
और false
साथ ही, वे एक सशर्त अभिव्यक्ति के रूप में भी कार्य करते हैं। वे डेटा और व्यवहार कर रहे हैं , एक में बँधा हुआ ... उह ... "बात" ... या (मुझे कहने की हिम्मत है) वस्तु !
वास्तव में, tru
और fls
वस्तुएं हैं। और, यदि आपने कभी स्मॉलटाक, सेल्फ, न्यूजक या अन्य ऑब्जेक्ट-ओरिएंटेड भाषाओं का उपयोग किया है, तो आपने देखा होगा कि वे बिल्कुल उसी तरह से बूलियन को लागू करते हैं। मैं यहां स्काला में इस तरह के कार्यान्वयन का प्रदर्शन करूंगा:
sealed abstract trait Buul {
def apply[T, U <: T, V <: T](thn: ⇒ U)(els: ⇒ V): T
def &&&(other: ⇒ Buul): Buul
def |||(other: ⇒ Buul): Buul
def ntt: Buul
}
case object Tru extends Buul {
override def apply[T, U <: T, V <: T](thn: ⇒ U)(els: ⇒ V): U = thn
override def &&&(other: ⇒ Buul) = other
override def |||(other: ⇒ Buul): this.type = this
override def ntt = Fls
}
case object Fls extends Buul {
override def apply[T, U <: T, V <: T](thn: ⇒ U)(els: ⇒ V): V = els
override def &&&(other: ⇒ Buul): this.type = this
override def |||(other: ⇒ Buul) = other
override def ntt = Tru
}
object BuulExtension {
import scala.language.implicitConversions
implicit def boolean2Buul(b: ⇒ Boolean) = if (b) Tru else Fls
}
import BuulExtension._
(2 < 3) { println("2 is less than 3") } { println("2 is greater than 3") }
// 2 is less than 3
यह BTW यही कारण है कि पॉलीमॉर्फ़िज्म रिफ्लेक्टिंग के साथ कंडिशनल रिप्लेसमेंट हमेशा काम करता है: आप हमेशा अपने प्रोग्राम में किसी भी और हर कंडीशनल को पॉलीमॉर्फिक मैसेज प्रेषण के साथ बदल सकते हैं, क्योंकि जैसा कि हमने अभी दिखाया है, पॉलीमॉर्फिक मैसेज चेक बस लागू करके सशर्त की जगह ले सकता है। स्मॉलटाक, सेल्फ और न्यूजक जैसी भाषाएं इस बात का अस्तित्व प्रमाण हैं, क्योंकि उन भाषाओं में भी सशर्त नहीं हैं। (उनके पास लूप्स, BTW या वास्तव में किसी भी प्रकार की भाषा में निर्मित नियंत्रण संरचना नहीं है, सिवाय बहुरूपिक संदेश के लिए उर्फ वर्चुअल मेथड कॉल्स प्रेषण।)
पैटर्न मिलान (हास्केल)
आप or
पैटर्न मिलान, या हास्केल की आंशिक फ़ंक्शन परिभाषा जैसी किसी चीज़ का उपयोग करके भी परिभाषित कर सकते हैं :
True ||| _ = True
_ ||| b = b
बेशक, पैटर्न मिलान सशर्त निष्पादन का एक रूप है, लेकिन फिर फिर से, वस्तु-उन्मुख संदेश प्रेषण है।