ठीक है!
नीचे दिए गए कोड को ES6 सिंटैक्स का उपयोग करके लिखा गया है लेकिन आसानी से ES5 या उससे भी कम में लिखा जा सकता है। ES6 "लूप x बार तंत्र" बनाने की आवश्यकता नहीं है
यदि आपको कॉलबैक में पुनरावृत्त की आवश्यकता नहीं है , तो यह सबसे सरल कार्यान्वयन है
const times = x => f => {
if (x > 0) {
f()
times (x - 1) (f)
}
}
// use it
times (3) (() => console.log('hi'))
// or define intermediate functions for reuse
let twice = times (2)
// twice the power !
twice (() => console.log('double vision'))
यदि आपको पुनरावृत्ति की आवश्यकता है, तो आप अपने लिए पुनरावृति करने के लिए काउंटर पैरामीटर के साथ नामित आंतरिक फ़ंक्शन का उपयोग कर सकते हैं
const times = n => f => {
let iter = i => {
if (i === n) return
f (i)
iter (i + 1)
}
return iter (0)
}
times (3) (i => console.log(i, 'hi'))
अगर आपको और चीजें सीखना पसंद नहीं है तो यहां पढ़ना बंद कर दें ...
लेकिन उन लोगों के बारे में कुछ महसूस करना चाहिए ...
- एकल शाखा
if
विवरण बदसूरत हैं - दूसरी शाखा पर क्या होता है?
- कार्य निकायों में कई कथन / भाव - क्या प्रक्रिया संबंधी चिंताएं मिश्रित हो रही हैं?
- निहित रूप से
undefined
- अशुद्ध, साइड-इफेक्टिंग फ़ंक्शन का संकेत
"क्या कोई बेहतर तरीका नहीं है?"
वहाँ है। आइए पहले हमारे प्रारंभिक कार्यान्वयन पर फिर से विचार करें
// times :: Int -> (void -> void) -> void
const times = x => f => {
if (x > 0) {
f() // has to be side-effecting function
times (x - 1) (f)
}
}
ज़रूर, यह सरल है, लेकिन ध्यान दें कि हम कैसे कॉल f()
करते हैं और इसके साथ कुछ भी नहीं करते हैं। यह वास्तव में फ़ंक्शन के प्रकार को सीमित करता है जिसे हम कई बार दोहरा सकते हैं। यहां तक कि अगर हमारे पास यह उपलब्ध है, f(i)
तो अधिक बहुमुखी नहीं है।
क्या होगा अगर हम एक बेहतर प्रकार के फ़ंक्शन पुनरावृत्ति प्रक्रिया के साथ शुरू करते हैं? शायद कुछ ऐसा जो इनपुट और आउटपुट का बेहतर उपयोग करता है।
सामान्य कार्य पुनरावृत्ति
// repeat :: forall a. Int -> (a -> a) -> a -> a
const repeat = n => f => x => {
if (n > 0)
return repeat (n - 1) (f) (f (x))
else
return x
}
// power :: Int -> Int -> Int
const power = base => exp => {
// repeat <exp> times, <base> * <x>, starting with 1
return repeat (exp) (x => base * x) (1)
}
console.log(power (2) (8))
// => 256
ऊपर, हमने एक सामान्य repeat
फ़ंक्शन को परिभाषित किया है जो एक अतिरिक्त इनपुट लेता है जिसका उपयोग किसी एकल फ़ंक्शन के दोहराए गए एप्लिकेशन को शुरू करने के लिए किया जाता है।
// repeat 3 times, the function f, starting with x ...
var result = repeat (3) (f) (x)
// is the same as ...
var result = f(f(f(x)))
के times
साथ कार्यान्वित कर रहा हैrepeat
खैर अब यह आसान है; लगभग सभी काम पहले ही हो चुके हैं।
// repeat :: forall a. Int -> (a -> a) -> a -> a
const repeat = n => f => x => {
if (n > 0)
return repeat (n - 1) (f) (f (x))
else
return x
}
// times :: Int -> (Int -> Int) -> Int
const times = n=> f=>
repeat (n) (i => (f(i), i + 1)) (0)
// use it
times (3) (i => console.log(i, 'hi'))
चूंकि हमारा फ़ंक्शन i
एक इनपुट के रूप में लेता है और रिटर्न i + 1
करता है, इसलिए यह प्रभावी रूप से हमारे पुनरावृत्त के रूप में काम करता है जिसे हम f
हर बार पास करते हैं।
हमने मुद्दों की अपनी बुलेट सूची भी तय कर दी है
- कोई और अधिक बदसूरत एकल शाखा
if
बयान नहीं
- एकल-अभिव्यक्ति निकाय अच्छी तरह से अलग चिंताओं का संकेत देते हैं
- अधिक बेकार नहीं, स्पष्ट रूप से वापस आ गया
undefined
जावास्क्रिप्ट अल्पविराम ऑपरेटर,
मामले में आपको यह देखने में परेशानी हो रही है कि अंतिम उदाहरण कैसे काम कर रहा है, यह जावास्क्रिप्ट के सबसे पुराने युद्ध कुल्हाड़ियों में से एक की आपकी जागरूकता पर निर्भर करता है; अल्पविराम ऑपरेटर - संक्षेप में, यह भाव बाएं से दाएं मूल्यांकन करता है और रिटर्न पिछले का मूल्यांकन अभिव्यक्ति के मूल्य
(expr1 :: a, expr2 :: b, expr3 :: c) :: c
हमारे उपरोक्त उदाहरण में, मैं उपयोग कर रहा हूं
(i => (f(i), i + 1))
जो लेखन का एक मात्र तरीका है
(i => { f(i); return i + 1 })
टेल कॉल ऑप्टिमाइज़ेशन
जैसा कि पुनरावर्ती कार्यान्वयन के रूप में सेक्सी हैं, इस बिंदु पर मेरे लिए यह सलाह देना गैरजिम्मेदार होगा कि कोई भी जावास्क्रिप्ट वीएम मैं उचित पूंछ कॉल उन्मूलन का समर्थन करने के बारे में नहीं सोच सकता - बेबल इसे ट्रांसपाइल करता था, लेकिन यह "टूटी हुई" में फिर से लागू होगा। "एक वर्ष से अधिक के लिए स्थिति।
repeat (1e6) (someFunc) (x)
// => RangeError: Maximum call stack size exceeded
जैसे, हमें repeat
इसे स्टैक-सेफ बनाने के लिए अपने कार्यान्वयन पर फिर से विचार करना चाहिए ।
नीचे दिया गया कोड उत्परिवर्तनीय चर का उपयोग करता हैn
और x
ध्यान दें कि सभी उत्परिवर्तन repeat
फ़ंक्शन में स्थानीय हैं - कोई भी राज्य परिवर्तन (म्यूटेशन) फ़ंक्शन के बाहर से दिखाई नहीं देता है
// repeat :: Int -> (a -> a) -> (a -> a)
const repeat = n => f => x =>
{
let m = 0, acc = x
while (m < n)
(m = m + 1, acc = f (acc))
return acc
}
// inc :: Int -> Int
const inc = x =>
x + 1
console.log (repeat (1e8) (inc) (0))
// 100000000
यह आपको बहुत कुछ कह रहा है "लेकिन यह कार्यात्मक नहीं है!" - मुझे पता है, बस आराम करो। हम शुद्ध भावों का उपयोग करते हुए निरंतर-स्पेस लूपिंग के लिए क्लोजर-स्टाइल loop
/ recur
इंटरफ़ेस लागू कर सकते हैं ; उस सामान में से कोई भी नहीं ।while
यहां हम while
अपने loop
फ़ंक्शन के साथ सार करते हैं - यह recur
लूप को चालू रखने के लिए एक विशेष प्रकार की तलाश करता है। जब एक गैर- recur
प्रकार का सामना किया जाता है, तो लूप समाप्त हो जाता है और गणना का परिणाम वापस आ जाता है
const recur = (...args) =>
({ type: recur, args })
const loop = f =>
{
let acc = f ()
while (acc.type === recur)
acc = f (...acc.args)
return acc
}
const repeat = $n => f => x =>
loop ((n = $n, acc = x) =>
n === 0
? acc
: recur (n - 1, f (acc)))
const inc = x =>
x + 1
const fibonacci = $n =>
loop ((n = $n, a = 0, b = 1) =>
n === 0
? a
: recur (n - 1, b, a + b))
console.log (repeat (1e7) (inc) (0)) // 10000000
console.log (fibonacci (100)) // 354224848179262000000