किसी फ़ंक्शन को परिभाषित करने के लिए "डीईएफ़" और "वैल" के बीच अंतर क्या है


214

दोनों के बीच क्या अंतर है:

def even: Int => Boolean = _ % 2 == 0

तथा

val even: Int => Boolean = _ % 2 == 0

दोनों को बुलाया जा सकता है even(10)


हाय, क्या Int => Booleanमतलब है? मुझे लगता है कि परिभाषित वाक्य रचना हैdef foo(bar: Baz): Bin = expr
Ziu

@Ziu का अर्थ है कि फ़ंक्शन 'सम' एक तर्क के रूप में एक Int प्राप्त करता है और एक बूलियन को मान प्रकार के रूप में वापस करता है। तो आप 'सम (3)' कह सकते हैं जो बूलियन को 'असत्य' का मूल्यांकन करता है
डेनिस लोबर

@DenysLobur आपके उत्तर के लिए धन्यवाद! इस वाक्यविन्यास के बारे में कोई संदर्भ?
जिउ

@Ziu मैं मूल रूप से इसे ओडस्की के क्रेसरा कोर्स से प्राप्त किया गया था - coursera.org/learn/progfun1 । जब आप इसे पूरा कर लेंगे, तब तक आप समझ जाएंगे कि 'टाइप => टाइप' का मतलब क्या है
डेनिस लोबर

जवाबों:


325

विधि def evenकॉल पर मूल्यांकन करती है और हर बार (नया उदाहरण Function1) नया फ़ंक्शन बनाती है ।

def even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = false

val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true

साथ defआप हर कॉल पर नया कार्य प्राप्त कर सकते हैं:

val test: () => Int = {
  val r = util.Random.nextInt
  () => r
}

test()
// Int = -1049057402
test()
// Int = -1049057402 - same result

def test: () => Int = {
  val r = util.Random.nextInt
  () => r
}

test()
// Int = -240885810
test()
// Int = -1002157461 - new result

valपरिभाषित होने पर मूल्यांकन करता है, def- जब बुलाया जाता है:

scala> val even: Int => Boolean = ???
scala.NotImplementedError: an implementation is missing

scala> def even: Int => Boolean = ???
even: Int => Boolean

scala> even
scala.NotImplementedError: an implementation is missing

ध्यान दें कि एक तीसरा विकल्प है lazy val:।

पहली बार कॉल करने पर यह मूल्यांकन करता है:

scala> lazy val even: Int => Boolean = ???
even: Int => Boolean = <lazy>

scala> even
scala.NotImplementedError: an implementation is missing

लेकिन FunctionNहर बार एक ही परिणाम (इस मामले में एक ही उदाहरण ) देता है:

lazy val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true

lazy val test: () => Int = {
  val r = util.Random.nextInt
  () => r
}

test()
// Int = -1068569869
test()
// Int = -1068569869 - same result

प्रदर्शन

val परिभाषित होने पर मूल्यांकन करता है।

defहर कॉल पर मूल्यांकन करता है, इसलिए प्रदर्शन valकई कॉल के लिए खराब हो सकता है । आपको एकल कॉल के साथ समान प्रदर्शन मिलेगा। और बिना किसी कॉल के आपको कोई ओवरहेड नहीं मिलेगा def, इसलिए आप इसे परिभाषित कर सकते हैं, भले ही आप इसे कुछ शाखाओं में उपयोग न करें।

एक साथ lazy valआप एक आलसी मूल्यांकन मिल जाएगा: आप इसे परिभाषित कर सकते हैं, भले ही आप कुछ शाखाओं में इसका इस्तेमाल नहीं होगा, और यह एक बार या कभी नहीं का मूल्यांकन करता है, लेकिन आप अपने लिए हर उपयोग पर दोहरी जांच लॉकिंग से थोड़ी भूमि के ऊपर मिल जाएगा lazy val

जैसा कि @SargeBorsch ने नोट किया कि आप विधि को परिभाषित कर सकते हैं, और यह सबसे तेज़ विकल्प है:

def even(i: Int): Boolean = i % 2 == 0

लेकिन अगर आपको फंक्शन कंपोजिशन के लिए फंक्शन (मेथड मेथड) की जरूरत है या हाई ऑर्डर फंक्शंस (जैसे filter(even)) कंपाइलर आपके फंक्शन से हर बार फंक्शन के रूप में यूज कर रहे हैं, तो परफॉर्मेंस थोड़ी खराब हो सकती है val


क्या आप कृपया प्रदर्शन के संबंध में उनकी तुलना करेंगे? क्या हर बार फ़ंक्शन को मूल्यांकन करना महत्वपूर्ण नहीं evenहै।
अमीर करीमी

2
defएक विधि को परिभाषित करने के लिए इस्तेमाल किया जा सकता है, और यह सबसे तेज़ विकल्प है। @ ए। करीमी
प्रदर्शन नाम

2
मस्ती के लिए: 2.12 पर, even eq even
सोम-संवत

क्या सी ++ में इनलाइन फ़ंक्शन की अवधारणा है? मैं सी ++ दुनिया से आ रहा हूं, इसलिए मेरी अज्ञानता को क्षमा करें।
एनिमेजोफमिन

2
@animageofmine स्काला संकलक इनलाइन तरीकों की कोशिश कर सकता है। इसके लिए @inlineविशेषता है। लेकिन यह इनलाइन फ़ंक्शन नहीं कर सकता क्योंकि फ़ंक्शन कॉल applyएक फ़ंक्शन ऑब्जेक्ट के वर्चुअल विधि के लिए कॉल है । JVM कुछ स्थितियों में ऐसे कॉलों को समर्पित और इनलाइन कर सकता है, लेकिन सामान्य रूप से नहीं।
सेनिया

24

इस पर विचार करो:

scala> def even: (Int => Boolean) = {
             println("def"); 
             (x => x % 2 == 0)
       }
even: Int => Boolean

scala> val even2: (Int => Boolean) = {
             println("val");
             (x => x % 2 == 0)
       }
val //gets printed while declaration. line-4
even2: Int => Boolean = <function1>

scala> even(1)
def
res9: Boolean = false

scala> even2(1)
res10: Boolean = false

आपको फर्क दिखता हैं? संक्षेप में:

def : हर कॉल के लिए even, यह evenविधि के शरीर को फिर से कॉल करता है । लेकिन साथ even2यानी वैल , समारोह केवल एक बार, जबकि घोषणा (और इसलिए यह प्रिंट आरंभ नहीं हो जाता valलाइन 4 पर और फिर कभी नहीं) और एक ही आउटपुट प्रत्येक बार इस तक पहुंच जाता है। उदाहरण के लिए ऐसा करने का प्रयास करें:

scala> import scala.util.Random
import scala.util.Random

scala> val x = { Random.nextInt }
x: Int = -1307706866

scala> x
res0: Int = -1307706866

scala> x
res1: Int = -1307706866

जब xप्रारंभ किया जाता है, Random.nextIntतो अंतिम मान के रूप में सेट किया गया मान लौटाया जाता है x। अगली बार xफिर से उपयोग किया जाता है, यह हमेशा समान मूल्य लौटाएगा।

आप आलसी को इनिशियलाइज़ भी कर सकते हैं x। अर्थात पहली बार इसका उपयोग किया जाता है, यह आरंभिक होता है और घोषणा के समय नहीं। उदाहरण के लिए:

scala> lazy val y = { Random.nextInt }
y: Int = <lazy>

scala> y
res4: Int = 323930673

scala> y
res5: Int = 323930673

6
मुझे लगता है कि आपका स्पष्टीकरण कुछ ऐसा हो सकता है जिसका आप इरादा नहीं करते हैं। even2दो बार कॉल करने की कोशिश करें , एक बार 1और एक बार साथ 2। आपको प्रत्येक कॉल में अलग-अलग उत्तर मिलेंगे। इसलिए, जबकि printlnबाद के कॉल में निष्पादित नहीं किया जाता है , तो आपको अलग-अलग कॉल से समान परिणाम नहीं मिलता है even2। जैसा कि printlnफिर से निष्पादित क्यों नहीं किया गया है, यह एक अलग सवाल है।
मेलस्टन

1
यह वास्तव में बहुत दिलचस्प है। यह वैली के मामले में भी है, यहाँ तक कि 2, वैल का मूल्यांकन एक मानदंड के लिए किया जाता है। तो हाँ एक वैल के साथ आप फ़ंक्शन का मूल्यांकन, उसका मूल्य। Println मूल्यांकन किए गए मूल्य का हिस्सा नहीं है। यह मूल्यांकन का हिस्सा है लेकिन मूल्यांकन मूल्य नहीं है। यहाँ चाल यह है कि मूल्यांकन किया गया मूल्य वास्तव में एक पैरामीरीकृत मूल्य है, जो कुछ इनपुट पर निर्भर करता है। स्मार्ट बात
MaatDeamon

1
@melston बिल्कुल! यह वही है जो मुझे समझ में आया है, इसलिए आउटपुट बदलते समय प्रिंटलाइन को फिर से निष्पादित नहीं किया जाता है?
और

1
@ और जो भी 2 से वापस आ गया है, वह वास्तव में एक फ़ंक्शन है (समभुज 2 की परिभाषा के अंत में कोष्ठक अभिव्यक्ति)। उस फ़ंक्शन को वास्तव में उस पैरामीटर के साथ बुलाया जाता है जिसे आप प्रत्येक बार पास करने के बाद भी 2 तक पास करते हैं।
मेल्स्टन जूल

5

यह देखो:

  var x = 2 // using var as I need to change it to 3 later
  val sq = x*x // evaluates right now
  x = 3 // no effect! sq is already evaluated
  println(sq)

हैरानी की बात है, यह 4 नहीं 9 मुद्रित करेगा! वैल (यहां तक ​​कि var) का मूल्यांकन तुरंत और सौंपा गया है।
अब बदलो वैल टू डिफ .. ये छपेगा 9! Def एक फंक्शन कॉल है .. यह हर बार मूल्यांकन करेगा जिसे यह कहा जाता है।


1

वैल यानी "वर्ग" स्कैला की परिभाषा के अनुसार है। यह घोषणा के समय सही मूल्यांकन किया जाता है, आप बाद में बदल नहीं सकते। अन्य उदाहरणों में, जहां सम 2 भी वैल है, लेकिन यह फंक्शन सिग्नेचर अर्थात "(इंट => बुलियन)" के साथ घोषित किया गया है, इसलिए यह इंट टाइप नहीं है। यह एक फंक्शन है और यह एक्सप्रेशन को फॉलो करके सेट होता है

   {
         println("val");
         (x => x % 2 == 0)
   }

स्काला वैली प्रॉपर्टी के अनुसार, आप एक और फंक्शन को सम 2 तक असाइन नहीं कर सकते हैं, एक ही नियम जैसे कि वर्ग।

Eval2 वैल फ़ंक्शन को कॉल करने के बारे में बार-बार "वैल" क्यों नहीं प्रिंट हो रहा है?

मूल कोड:

val even2: (Int => Boolean) = {
             println("val");
             (x => x % 2 == 0)
       }

हम जानते हैं, स्लाला के अंतिम कथन में उपरोक्त प्रकार की अभिव्यक्ति ({..} के अंदर) वास्तव में बाएं हाथ की ओर है। तो आप अंत में समरूप 2 को "x => x% 2 == 0" फ़ंक्शन पर सेट करते हैं, जो आपके द्वारा समान रूप से 2 वेल प्रकार (यानी => बूलियन) के लिए घोषित प्रकार से मेल खाता है, इसलिए कंपाइलर खुश है। अब भी 2 केवल "(x => x% 2 == 0)" फ़ंक्शन को इंगित करता है (अर्थात प्रिंटनल ("वैल") से पहले कोई अन्य बयान नहीं) विभिन्न मापदंडों के साथ इवेंट 2 को लागू करना वास्तव में लागू होगा "(x => x% 2 == 0) "कोड, जो केवल event2 के साथ सहेजा जाता है।

scala> even2(2)
res7: Boolean = true

scala> even2(3)
res8: Boolean = false

बस इसे और अधिक स्पष्ट करने के लिए, निम्नलिखित कोड के विभिन्न संस्करण हैं।

scala> val even2: (Int => Boolean) = {
     |              println("val");
     |              (x => { 
     |               println("inside final fn")
     |               x % 2 == 0
     |             })
     |        }

क्या होगा ? यहां हम बार-बार प्रिंट किए गए "इनसाइड फ़ाइनल fn" को देखते हैं, जब आप इवन 2 () कहते हैं।

scala> even2(3)
inside final fn
res9: Boolean = false

scala> even2(2)
inside final fn
res10: Boolean = true

scala> 

1

परिभाषा को निष्पादित करना जैसे def x = eकि अभिव्यक्ति का मूल्यांकन नहीं करेगा ई। जब भी x का आह्वान किया जाता है, तब स्थिर ई का मूल्यांकन किया जाता है।

वैकल्पिक रूप से, स्काला एक मूल्य परिभाषा प्रदान करता है val x = e, जो परिभाषा के मूल्यांकन के भाग के रूप में दाईं ओर का मूल्यांकन करता है। यदि बाद में एक्स का उपयोग किया जाता है, तो इसे तुरंत ई के पूर्व-गणना मूल्य द्वारा बदल दिया जाता है, ताकि अभिव्यक्ति का फिर से मूल्यांकन करने की आवश्यकता न हो।


0

इसके अलावा, वैल एक वैल्यू मूल्यांकन है। जिसका अर्थ है कि दाएं हाथ की अभिव्यक्ति का मूल्यांकन परिभाषा के दौरान किया जाता है। जहां Def का नाम मूल्यांकन से होता है। इसका उपयोग करने तक इसका मूल्यांकन नहीं होगा।


0

उपरोक्त सहायक उत्तरों के अलावा, मेरे निष्कर्ष हैं:

def test1: Int => Int = {
x => x
}
--test1: test1[] => Int => Int

def test2(): Int => Int = {
x => x+1
}
--test2: test2[]() => Int => Int

def test3(): Int = 4
--test3: test3[]() => Int

ऊपर दिखाया गया है कि "डीफ़" एक विधि है (शून्य तर्क मापदंडों के साथ) जो एक और फ़ंक्शन "Int => Int" देता है जब आह्वान किया जाता है।

कार्यों के तरीकों का रूपांतरण यहाँ अच्छी तरह से समझाया गया है: https://tpolecat.github.io/2014/06/09/methods-functions.html


0

REPL में,

scala> def even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean

scala> val even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean = $$Lambda$1157/1017502292@57a0aeb8

def का मतलब है call-by-name, मांग पर मूल्यांकन

वैल का मतलब है call-by-value, प्रारंभिक मूल्यांकन करते समय मूल्यांकन किया गया


एक प्रश्न के साथ यह पुराना है, और पहले से ही प्रस्तुत किए गए इतने सारे उत्तरों के साथ, यह अक्सर यह समझाने में मददगार होता है कि आपका उत्तर किस प्रकार अलग है, या मौजूदा उत्तरों में दी गई जानकारी को जोड़ता है।
jwvh
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.