सार्थक उदाहरणों के माध्यम से स्काला निरंतरता
हमें परिभाषित करते हैं from0to10
कि 0 से 10 तक पुनरावृति के विचार को व्यक्त करता है:
def from0to10() = shift { (cont: Int => Unit) =>
for ( i <- 0 to 10 ) {
cont(i)
}
}
अभी,
reset {
val x = from0to10()
print(s"$x ")
}
println()
प्रिंट:
0 1 2 3 4 5 6 7 8 9 10
वास्तव में, हमें इसकी आवश्यकता नहीं है x
:
reset {
print(s"${from0to10()} ")
}
println()
एक ही परिणाम प्रिंट करता है।
तथा
reset {
print(s"(${from0to10()},${from0to10()}) ")
}
println()
सभी जोड़े प्रिंट:
(0,0) (0,1) (0,2) (0,3) (0,4) (0,5) (0,6) (0,7) (0,8) (0,9) (0,10) (1,0) (1,1) (1,2) (1,3) (1,4) (1,5) (1,6) (1,7) (1,8) (1,9) (1,10) (2,0) (2,1) (2,2) (2,3) (2,4) (2,5) (2,6) (2,7) (2,8) (2,9) (2,10) (3,0) (3,1) (3,2) (3,3) (3,4) (3,5) (3,6) (3,7) (3,8) (3,9) (3,10) (4,0) (4,1) (4,2) (4,3) (4,4) (4,5) (4,6) (4,7) (4,8) (4,9) (4,10) (5,0) (5,1) (5,2) (5,3) (5,4) (5,5) (5,6) (5,7) (5,8) (5,9) (5,10) (6,0) (6,1) (6,2) (6,3) (6,4) (6,5) (6,6) (6,7) (6,8) (6,9) (6,10) (7,0) (7,1) (7,2) (7,3) (7,4) (7,5) (7,6) (7,7) (7,8) (7,9) (7,10) (8,0) (8,1) (8,2) (8,3) (8,4) (8,5) (8,6) (8,7) (8,8) (8,9) (8,10) (9,0) (9,1) (9,2) (9,3) (9,4) (9,5) (9,6) (9,7) (9,8) (9,9) (9,10) (10,0) (10,1) (10,2) (10,3) (10,4) (10,5) (10,6) (10,7) (10,8) (10,9) (10,10)
अब, वह कैसे काम करता है?
नहीं है कहा जाता है कोड , from0to10
और बुला कोड । इस मामले में, यह ब्लॉक है जो निम्नानुसार है reset
। बुलाया कोड में पारित मापदंडों में से एक एक वापसी पता है जो दिखाता है कि कॉलिंग कोड का क्या हिस्सा अभी तक निष्पादित नहीं किया गया है (**)। कॉलिंग कोड का वह हिस्सा निरंतरता है । कहा जाता है कि कोड उस पैरामीटर के साथ कर सकता है जो कुछ भी यह तय करता है: इसे करने के लिए नियंत्रण, या इसे अनदेखा करें, या इसे कई बार कॉल करें। यहां from0to10
उस पूर्णांक के लिए निरंतरता को ०.१० कहा जाता है।
def from0to10() = shift { (cont: Int => Unit) =>
for ( i <- 0 to 10 ) {
cont(i)
}
}
लेकिन निरंतरता कहां समाप्त होती है? यह महत्वपूर्ण है क्योंकि return
निरंतरता से अंतिम नियंत्रण को कोड कहा जाता है from0to10
। स्काला में, वह समाप्त होता है जहां reset
ब्लॉक समाप्त होता है (*)।
अब, हम देखते हैं कि निरंतरता के रूप में घोषित किया गया है cont: Int => Unit
। क्यों? हम के from0to10
रूप में आह्वान करते हैं val x = from0to10()
, और Int
उस मूल्य का प्रकार है जो जाता है x
। Unit
इसका मतलब है कि ब्लॉक के बाद reset
कोई मान नहीं लौटना चाहिए (अन्यथा एक प्रकार की त्रुटि होगी)। सामान्य तौर पर, 4 प्रकार के हस्ताक्षर होते हैं: फ़ंक्शन इनपुट, निरंतरता इनपुट, निरंतरता परिणाम, फ़ंक्शन परिणाम। चारों को आह्वान के संदर्भ से मेल खाना चाहिए।
ऊपर, हमने मानों के जोड़े मुद्रित किए। हमें गुणा तालिका प्रिंट करें। लेकिन हम \n
प्रत्येक पंक्ति के बाद आउटपुट कैसे करते हैं ?
फ़ंक्शन back
हमें यह निर्दिष्ट करने देता है कि नियंत्रण वापस आने पर क्या किया जाना चाहिए, निरंतरता से उस कोड तक जो इसे कहा जाता है।
def back(action: => Unit) = shift { (cont: Unit => Unit) =>
cont()
action
}
back
पहले अपनी निरंतरता को बुलाता है, और फिर कार्रवाई करता है ।
reset {
val i = from0to10()
back { println() }
val j = from0to10
print(f"${i*j}%4d ")
}
यह प्रिंट करता है:
0 0 0 0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7 8 9 10
0 2 4 6 8 10 12 14 16 18 20
0 3 6 9 12 15 18 21 24 27 30
0 4 8 12 16 20 24 28 32 36 40
0 5 10 15 20 25 30 35 40 45 50
0 6 12 18 24 30 36 42 48 54 60
0 7 14 21 28 35 42 49 56 63 70
0 8 16 24 32 40 48 56 64 72 80
0 9 18 27 36 45 54 63 72 81 90
0 10 20 30 40 50 60 70 80 90 100
खैर, अब कुछ ब्रेन-ट्विस्ट का समय है। के दो आह्वान हैं from0to10
। पहले के लिए निरंतरता क्या है from0to10
? यह इस प्रकार के आह्वान from0to10
में बाइनरी कोड , लेकिन स्रोत कोड में भी काम बयान भी शामिल है val i =
। यह समाप्त होता है जहां reset
ब्लॉक समाप्त होता है, लेकिन ब्लॉक का अंत reset
पहले पर नियंत्रण वापस नहीं करता है from0to10
। reset
ब्लॉक का अंत 2 पर नियंत्रण लौटाता है from0to10
, जो अंत में नियंत्रण को वापस लौटाता है back
, और यह है back
कि पहले के आह्वान पर नियंत्रण लौटाता है from0to10
। जब पहला (हाँ! पहला!) from0to10
बाहर निकलता है, तो पूरा reset
ब्लॉक बाहर निकल जाता है।
नियंत्रण वापस करने की ऐसी विधि को बैकट्रैकिंग कहा जाता है , यह एक बहुत पुरानी तकनीक है, जिसे कम से कम प्रोलॉग और एआई-उन्मुख लिस्प डेरिवेटिव के समय से जाना जाता है।
नाम reset
और shift
मिथ्या नाम हैं। इन नामों को बिटवाइज़ ऑपरेशन के लिए बेहतर छोड़ दिया जाना चाहिए था। reset
निरंतरता सीमाओं को परिभाषित करता है, और shift
कॉल स्टैक से एक निरंतरता लेता है।
टिप्पणियाँ)
(*) स्काला में, जहां reset
ब्लॉक समाप्त होता है, निरंतरता समाप्त होती है। एक और संभावित दृष्टिकोण यह होगा कि इसे समाप्त करें जहां फ़ंक्शन समाप्त होता है।
(**) कॉल कोड के मापदंडों में से एक रिटर्न एड्रेस है जो दिखाता है कि कॉलिंग कोड के किस हिस्से को अभी तक निष्पादित नहीं किया गया है। खैर, स्काला में, रिटर्न एड्रेस का एक क्रम इसके लिए उपयोग किया जाता है। कितने? reset
ब्लॉक में प्रवेश करने के बाद से कॉल स्टैक पर रखे गए सभी वापसी पते ।
UPD भाग 2
जारी रखना: छानना
def onEven(x:Int) = shift { (cont: Unit => Unit) =>
if ((x&1)==0) {
cont()
}
}
reset {
back { println() }
val x = from0to10()
onEven(x)
print(s"$x ")
}
यह प्रिंट:
0 2 4 6 8 10
आइए हम दो महत्वपूर्ण कार्यकलापों को कार्यान्वित करें: निरंतरता को त्यागना ( fail()
) और उस पर नियंत्रण को पार करना ( succ()
):
def fail() = shift { (cont: Unit => Unit) => }
def succ():Unit @cpsParam[Unit,Unit] = { }
succ()
(ऊपर) के दोनों संस्करण काम करते हैं। यह पता चला है कि shift
एक मजाकिया हस्ताक्षर है, और हालांकि succ()
कुछ भी नहीं है, यह प्रकार संतुलन के लिए उस हस्ताक्षर होना चाहिए।
reset {
back { println() }
val x = from0to10()
if ((x&1)==0) {
succ()
} else {
fail()
}
print(s"$x ")
}
जैसा कि अपेक्षित है, यह प्रिंट करता है
0 2 4 6 8 10
एक फ़ंक्शन के भीतर, succ()
आवश्यक नहीं है:
def onTrue(b:Boolean) = {
if(!b) {
fail()
}
}
reset {
back { println() }
val x = from0to10()
onTrue ((x&1)==0)
print(s"$x ")
}
फिर से, यह प्रिंट करता है
0 2 4 6 8 10
अब, हम इसके onOdd()
माध्यम से परिभाषित करते हैं onEven()
:
class ControlTransferException extends Exception {}
def onOdd(x:Int) = shift { (cont: Unit => Unit) =>
try {
reset {
onEven(x)
throw new ControlTransferException()
}
cont()
} catch {
case e: ControlTransferException =>
case t: Throwable => throw t
}
}
reset {
back { println() }
val x = from0to10()
onOdd(x)
print(s"$x ")
}
ऊपर, अगर x
यह भी है, तो एक अपवाद फेंक दिया जाता है और निरंतरता को नहीं कहा जाता है; यदि x
विषम है, तो अपवाद नहीं फेंका जाता है और निरंतरता कहा जाता है। उपरोक्त कोड प्रिंट:
1 3 5 7 9