=>, () =>, और इकाई => में क्या अंतर है


153

मैं एक ऐसे फ़ंक्शन का प्रतिनिधित्व करने की कोशिश कर रहा हूं जो कोई तर्क नहीं देता है और कोई मूल्य नहीं देता है (यदि आप जानते हैं तो मैं जावास्क्रिप्ट में सेटटाइमआउट फ़ंक्शन का अनुकरण कर रहा हूं।)

case class Scheduled(time : Int, callback :  => Unit)

"वैल 'पैरामीटर को कॉल-बाय-नेम नहीं कहा जा सकता है

case class Scheduled(time : Int, callback :  () => Unit)  

संकलन करता है, लेकिन इसके बजाय अजीब तरीके से आह्वान करना पड़ता है

Scheduled(40, { println("x") } )

मुझे यह करना है

Scheduled(40, { () => println("x") } )      

क्या काम करता है

class Scheduled(time : Int, callback :  Unit => Unit)

लेकिन एक सम-कम-समझदार तरीके से लागू किया जाता है

 Scheduled(40, { x : Unit => println("x") } )

(प्रकार इकाई का एक चर क्या होगा?) जो मैं निश्चित रूप से चाहता हूं वह एक निर्माता है जो उस तरह से आह्वान कर सकता है जैसे कि यह एक सामान्य कार्य था।

 Scheduled(40, println("x") )

बच्चे को उसकी बोतल दे दो!


3
उप-नाम के साथ केस कक्षाओं का उपयोग करने का एक और तरीका है, उन्हें एक माध्यमिक पैरामीटर सूची में रखा जाता है, जैसे case class Scheduled(time: Int)(callback: => Unit)। यह काम करता है क्योंकि माध्यमिक पैरामीटर सूची सार्वजनिक रूप से उजागर नहीं होती है, और न ही इसे उत्पन्न equals/ hashCodeविधियों में शामिल किया जाता है ।
nilskp

इस प्रश्न और उत्तर में उप-नाम पैरामीटर और 0-एरिटी फ़ंक्शन के बीच अंतर के बारे में कुछ और दिलचस्प पहलू पाए जाते हैं । यह वास्तव में वह है जिसे मैं खोज रहा था जब मुझे यह प्रश्न मिला।
13:82 पर lex82

जवाबों:


234

कॉल-बाय-नाम: => प्रकार

=> Typeअंकन कॉल-दर-नाम, जिनमें से एक है के लिए खड़ा है कई मायनों मापदंडों पारित किया जा सकता। यदि आप उनसे परिचित नहीं हैं, तो मैं उस विकिपीडिया लेख को पढ़ने के लिए कुछ समय लेने की सलाह देता हूं, भले ही आजकल यह ज्यादातर कॉल-बाय-वैल्यू और कॉल-बाय-रेफरेंस हो।

इसका मतलब यह है कि जो पास किया गया है वह फ़ंक्शन के अंदर मूल्य नाम के लिए प्रतिस्थापित किया गया है। उदाहरण के लिए, इस फ़ंक्शन को लें:

def f(x: => Int) = x * x

अगर मैं इसे इस तरह कहता हूं

var y = 0
f { y += 1; y }

फिर कोड इस तरह निष्पादित होगा

{ y += 1; y } * { y += 1; y }

हालांकि अगर वहाँ एक पहचानकर्ता नाम टकराव होता है तो क्या होता है। पारंपरिक कॉल-बाय-नेम में, नाम क्लैश से बचने के लिए कैप्चर से बचने वाला प्रतिस्थापन नामक एक तंत्र होता है। स्काला में, हालांकि, इसे एक ही परिणाम के साथ एक और तरीके से लागू किया जाता है - पैरामीटर के अंदर पहचानकर्ता नाम जिन्हें फ़ंक्शन में पहचान या छायांकन नहीं किया जा सकता है।

कॉल-बाय-नाम से संबंधित कुछ अन्य बिंदु हैं जो मैं अन्य दो के बारे में बताने के बाद बोलूंगा।

0-एरिटी फंक्शन्स: () => टाइप

वाक्य रचना () => Typeएक के प्रकार के लिए खड़ा है Function0। यही है, एक फ़ंक्शन जो कोई पैरामीटर नहीं लेता है और कुछ लौटाता है। यह विधि कहने के बराबर है size()- यह कोई पैरामीटर नहीं लेता है और एक नंबर लौटाता है।

हालांकि, यह दिलचस्प है कि यह सिंटैक्स एक अनाम फ़ंक्शन शाब्दिक के लिए सिंटैक्स के समान है , जो कुछ भ्रम का कारण है। उदाहरण के लिए,

() => println("I'm an anonymous function")

एक गुमनाम फंक्शन शाब्दिक है, जिसका प्रकार ० है

() => Unit

तो हम लिख सकते हैं:

val f: () => Unit = () => println("I'm an anonymous function")

हालांकि, मूल्य के साथ प्रकार को भ्रमित नहीं करना महत्वपूर्ण है।

इकाई => प्रकार

यह वास्तव में सिर्फ एक है Function1, जिसका पहला पैरामीटर प्रकार का है Unit। इसे लिखने के अन्य तरीके (Unit) => Typeया होंगे Function1[Unit, Type]। बात यह है ... यह कभी होने की संभावना नहीं है कि कोई क्या चाहता है। Unitप्रकार का मुख्य उद्देश्य एक मूल्य एक में कोई दिलचस्पी नहीं है का संकेत है, इसलिए कोई मतलब नहीं है करने के लिए प्राप्त है कि मूल्य।

उदाहरण के लिए विचार करें,

def f(x: Unit) = ...

संभवतः कोई क्या कर सकता है x? इसका केवल एक ही मूल्य हो सकता है, इसलिए किसी को इसे प्राप्त करने की आवश्यकता नहीं है। एक संभावित उपयोग के लिए वापसी के कार्यों का पीछा किया जाएगा Unit:

val f = (x: Unit) => println("I'm f")
val g = (x: Unit) => println("I'm g")
val h = f andThen g

क्योंकि andThenकेवल पर परिभाषित किया गया है Function1, और जिन कार्यों को हम कर रहे हैं वे वापस लौट रहे हैं Unit, हमें उन्हें Function1[Unit, Unit]श्रृंखलाबद्ध करने में सक्षम होने के लिए उन्हें परिभाषित करना होगा।

भ्रम के स्रोत

भ्रम का पहला स्रोत प्रकार और शाब्दिक के बीच समानता है जो 0-एरिटी फ़ंक्शन के लिए मौजूद है, कॉल-बाय-नाम के लिए भी मौजूद है। दूसरे शब्दों में, यह सोचकर, क्योंकि

() => { println("Hi!") }

एक शाब्दिक के लिए है () => Unit, तो

{ println("Hi!") }

के लिए शाब्दिक होगा => Unit। यह नहीं। वह कोड का एक ब्लॉक है , न कि शाब्दिक।

भ्रम का एक अन्य स्रोत Unitप्रकार है मान लिखा जाता है (), जो 0-एरिटी पैरामीटर सूची की तरह दिखता है (लेकिन ऐसा नहीं है)।


मुझे दो साल बाद पहली बार डाउन-वोट करना पड़ सकता है। कोई व्यक्ति क्रिसमस पर वाक्य => वाक्यविन्यास के बारे में सोच रहा है, और मैं इस उत्तर को विहित और पूर्ण के रूप में सुझा नहीं सकता हूँ! दुनिया कहां जा रही है? हो सकता है कि मायावादियों को बस एक सप्ताह से बंद कर दिया गया था। क्या उन्होंने लीप वर्षों में सही ढंग से आंकड़ा किया है? दिन के उजाले की बचत?
सोम-संवत

@ som-snytt वैसे, इस बारे में सवाल नहीं पूछा गया था case ... =>, इसलिए मैंने इसका उल्लेख नहीं किया। दुखद लेकिन सत्य। :-)
डैनियल सी। सोबरल

1
@ डैनियल सी। सोबरल कृपया आपको समझा सकते हैं "यह कोड का एक ब्लॉक है, न कि शाब्दिक।" अंश। तो दो के बीच सटीक भिन्न क्या है?
nish1013

2
@ nish1013 एक "शाब्दिक" एक मूल्य ( कुछ उदाहरणों के लिए पूर्णांक 1, वर्ण 'a', स्ट्रिंग "abc"या फ़ंक्शन () => println("here")) है। इसे तर्क के रूप में पारित किया जा सकता है, चर में संग्रहीत किया जाता है, आदि "कोड का ब्लॉक" बयानों का एक क्रमिक परिसीमन है - यह कोई मूल्य नहीं है, इसे पास नहीं किया जा सकता है, या ऐसा कुछ भी नहीं किया जा सकता है।
डैनियल सी। सोबरल

1
यही कारण है कि के रूप में ही अंतर है @Alex (Unit) => Typeबनाम () => Typeपहले एक है - Function1[Unit, Type]जबकि दूसरा एक है, Function0[Type]
डैनियल सी। सोब्राल

36
case class Scheduled(time : Int, callback :  => Unit)

caseसंशोधक निहित बनाता valनिर्माता के लिए प्रत्येक तर्क से बाहर। इसलिए (जैसा कि किसी ने उल्लेख किया है) यदि आप हटाते caseहैं तो आप कॉल-बाय-नेम पैरामीटर का उपयोग कर सकते हैं। संकलक शायद इसे वैसे भी अनुमति दे सकता है, लेकिन यह लोगों को आश्चर्यचकित कर सकता है अगर इसे बनाने के val callbackबजाय इसे बनाया जाएlazy val callback

जब आप callback: () => Unitअब अपने मामले में बदल जाते हैं तो कॉल-बाय-नेम पैरामीटर के बजाय केवल एक फ़ंक्शन होता है। जाहिर है कि फंक्शन को स्टोर किया जा सकता है, val callbackइसलिए कोई समस्या नहीं है।

आप जो चाहते हैं उसे प्राप्त करने का सबसे आसान तरीका ( Scheduled(40, println("x") )जहां एक कॉल-बाय-नेम पैरामीटर का उपयोग लैम्बडा को पास करने के लिए किया जाता है) संभवतः इसे छोड़ना caseऔर स्पष्ट रूप से बनाना है applyजो आपको पहले स्थान पर नहीं मिल सकता है:

class Scheduled(val time: Int, val callback: () => Unit) {
    def doit = callback()
}

object Scheduled {
    def apply(time: Int, callback: => Unit) =
        new Scheduled(time, { () => callback })
}

उपयोग में:

scala> Scheduled(1234, println("x"))
res0: Scheduled = Scheduled@5eb10190

scala> Scheduled(1234, println("x")).doit
x

3
इसे केस-क्लास क्यों नहीं रखा और सिर्फ डिफॉल्ट लागू को खत्म नहीं किया? इसके अलावा, कंपाइलर एक आलसी वैली को बाय-नाम का अनुवाद नहीं कर सकता है, क्योंकि उनके पास स्वाभाविक रूप से अलग-अलग शब्दार्थ हैं, आलसी कम से कम एक बार और उप-नाम का हर संदर्भ में है
विक्टर क्लैंग

@ViktorKlang आप किसी केस क्लास की डिफ़ॉल्ट लागू पद्धति को कैसे ओवरराइड कर सकते हैं? stackoverflow.com/questions/2660975/…
Sawyer

ऑब्जेक्ट क्लासनेम {डिफ अप्लाई (…):… =…}
विक्टर क्लैंग

चार साल बाद और मुझे एहसास हुआ कि मैंने जो उत्तर चुना था, उसने शीर्षक में केवल प्रश्न का उत्तर दिया था, न कि मेरे पास वास्तव में (जिसका यह उत्तर है)।
मालवोलियो

1

सवाल में, आप जावास्क्रिप्ट में SetTimeOut फ़ंक्शन का अनुकरण करना चाहते हैं। पिछले उत्तरों के आधार पर, मैं निम्नलिखित कोड लिखता हूं:

class Scheduled(time: Int, cb: => Unit) {
  private def runCb = cb
}

object Scheduled {
  def apply(time: Int, cb: => Unit) = {
    val instance = new Scheduled(time, cb)
    Thread.sleep(time*1000)
    instance.runCb
  }
}

REPL में, हम कुछ इस तरह से प्राप्त कर सकते हैं:

scala> Scheduled(10, println("a")); Scheduled(1, println("b"))
a
b

हमारा सिमुलेशन बिल्कुल SetTimeOut की तरह व्यवहार नहीं करता है, क्योंकि हमारा सिमुलेशन फ़ंक्शन को रोक रहा है, लेकिन SetTimeOut गैर-अवरुद्ध है।


0

मैं इसे इस तरह से करता हूं (बस आवेदन को तोड़ना नहीं चाहता):

case class Thing[A](..., lazy: () => A) {}
object Thing {
  def of[A](..., a: => A): Thing[A] = Thing(..., () => a)
}

और इसे कॉल करें

Thing.of(..., your_value)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.