असाइन किए गए मान के बजाय यूनिट को मूल्यांकन करने वाले स्काला असाइनमेंट की प्रेरणा क्या है?


84

असाइन किए गए मान के बजाय यूनिट को मूल्यांकन करने वाले स्काला असाइनमेंट की प्रेरणा क्या है?

I / O प्रोग्रामिंग में एक सामान्य पैटर्न इस तरह की चीजें करना है:

while ((bytesRead = in.read(buffer)) != -1) { ...

लेकिन स्काला में ऐसा संभव नहीं है क्योंकि ...

bytesRead = in.read(buffer)

.. यूनिट लौटाता है, बाइट्स का नया मूल्य नहीं।

एक कार्यात्मक भाषा से बाहर निकलने के लिए एक दिलचस्प चीज की तरह लगता है। मैं सोच रहा हूं कि ऐसा क्यों किया गया?


डेविड पोलाक ने कुछ फर्स्ट-हैंड जानकारी पोस्ट की हैं, जो मार्टिन ओडस्की की टिप्पणी से काफी हद तक ख़ुद को अपने जवाब पर छोड़ देता है। मुझे लगता है कि कोई भी पोलक के जवाब को सुरक्षित रूप से स्वीकार कर सकता है।
डैनियल सी। सोबरल

जवाबों:


88

मैंने असाइनमेंट के लिए वकालत की बजाय यूनिट के बदले दिए गए मूल्य को वापस कर दिया। मार्टिन और मैं उस पर आगे और पीछे चले गए, लेकिन उनका तर्क यह था कि स्टैक पर एक मूल्य डालकर इसे 95% से दूर करने के लिए बाइट-कोड की बर्बादी थी और प्रदर्शन पर नकारात्मक प्रभाव पड़ता है।


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

43
बसने वालों की उपस्थिति में यह इतना आसान नहीं है: प्रत्येक सेटर को एक परिणाम वापस करना होगा, जो लिखने के लिए एक दर्द है। फिर कंपाइलर को इसे दूर ऑप्टिमाइज़ करना पड़ता है, जिसे कॉल के दौरान करना मुश्किल है।
मार्टिन ओडस्की

1
आपका तर्क समझ में आता है, फिर भी जावा और सी # उस के खिलाफ हैं। मुझे लगता है कि आप उत्पन्न बाइट कोड के साथ कुछ अजीब कर रहे हैं, तो स्कैला में असाइनमेंट को क्लास फाइल में कैसे संकलित किया जाएगा और जावा के लिए विघटित वापस दिखाई देगा?
नग्यन

3
@ Phư isngNguyễn अंतर यूनिफॉर्म एक्सेस सिद्धांत है। C # / Java में (आमतौर पर) वापसी होती है void। अगर काम करता है तो स्काला में foo_=(v: Foo)वापस आना चाहिए Foo
एलेक्सी रोमनोव

5
@ मर्टिन ओडस्की: कैसे के बारे में निम्नलिखित: बसने रहते हैं void( Unit), असाइनमेंट के x = valueबराबर में अनुवादित हो x.set(value);x.get(value); संकलक चरणों का अनुकूलन करने में समाप्त कर देता है- getयदि मूल्य अप्रयुक्त था। यह एक नए प्रमुख (पिछड़े असंगतता के कारण) में एक स्वागत योग्य बदलाव हो सकता है, स्काला रिलीज़ और उपयोगकर्ताओं के लिए कम चिड़चिड़ाहट। तुम क्या सोचते हो?
यूजेन लाबून

20

मैं वास्तविक कारणों की जानकारी के अंदर निजी नहीं हूँ, लेकिन मेरा संदेह बहुत सरल है। स्काला साइड-इफ़ेक्टफुल लूप्स का उपयोग करने के लिए अजीब बनाता है ताकि प्रोग्रामर स्वाभाविक रूप से समझने के लिए पसंद करेंगे।

यह कई तरीकों से ऐसा करता है। उदाहरण के लिए, आपके पास एक forलूप नहीं है जहां आप एक चर घोषित करते हैं और म्यूट करते हैं। आप एक whileही समय में (आसानी से) लूप पर उत्परिवर्तित नहीं कर सकते हैं उसी समय जब आप स्थिति का परीक्षण करते हैं, जिसका अर्थ है कि आपको अक्सर इसके ठीक पहले और इसके अंत में उत्परिवर्तन को दोहराना होगा। एक whileब्लॉक के अंदर घोषित चर whileपरीक्षण की स्थिति से दिखाई नहीं देते हैं, जो do { ... } while (...)बहुत कम उपयोगी बनाता है। और इसी तरह।

युक्ति:

while ({bytesRead = in.read(buffer); bytesRead != -1}) { ... 

इसके लायक जो भी हो।

एक वैकल्पिक व्याख्या के रूप में, शायद मार्टिन ओडस्की को इस तरह के उपयोग से प्राप्त होने वाले कुछ बहुत ही बदसूरत कीड़े का सामना करना पड़ा, और इसे अपनी भाषा से अलग करने का फैसला किया।

संपादित करें

डेविड पोलाक ने कुछ वास्तविक तथ्यों के साथ उत्तर दिया है , जो इस तथ्य से स्पष्ट रूप से पुष्ट हैं कि मार्टिन ओडस्की ने स्वयं अपने जवाब में टिप्पणी की थी, जो कि पोलाक द्वारा डाले गए प्रदर्शन-संबंधी मुद्दों के तर्क को विश्वसनीयता प्रदान करता है।


3
तो शायद forपाश संस्करण होगा: for (bytesRead <- in.read(buffer) if (bytesRead) != -1जो सिवाय इसके कि यह काम नहीं करेगा महान है क्योंकि वहाँ कोई foreachऔर withFilterउपलब्ध है!
oxbow_lakes 12

12

यह स्काला के एक अधिक "औपचारिक रूप से सही" प्रकार प्रणाली वाले हिस्से के रूप में हुआ। औपचारिक रूप से बोलना, असाइनमेंट एक विशुद्ध रूप से साइड-इफेक्टिंग स्टेटमेंट है और इसलिए इसे वापस आ जाना चाहिए Unit। इसके कुछ अच्छे परिणाम हैं; उदाहरण के लिए:

class MyBean {
  private var internalState: String = _

  def state = internalState

  def state_=(state: String) = internalState = state
}

state_=विधि रिटर्न Unit(के रूप में एक सेटर के लिए उम्मीद होगी) इसलिए हुआ क्योंकि उस काम रिटर्न Unit

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


धन्यवाद, डैनियल। मुझे लगता है कि मैं इसे पसंद करूंगा अगर स्थिरता यह थी कि दोनों असाइनमेंट और सेटर ने मान लौटा दिया! (कोई कारण नहीं है कि वे नहीं कर सकते।) मुझे संदेह है कि मैं "विशुद्ध रूप से साइड-इफेक्टिंग स्टेटमेंट" जैसी अवधारणाओं की बारीकियों को अभी तक नहीं कर रहा हूं।
ग्राहम ले

2
@ ग्राहम: लेकिन फिर, आपको निरंतरता का पालन करना होगा और अपने सभी बाशिंदों में यह सुनिश्चित करना होगा कि वे कितने भी जटिल हों, लेकिन वे अपने द्वारा निर्धारित मूल्य को वापस कर देते हैं। यह कुछ मामलों में जटिल होगा और अन्य मामलों में गलत होगा, मुझे लगता है। (आप त्रुटि के मामले में क्या लौटाएंगे? अशक्त? - बल्कि नहीं। कोई नहीं? - फिर आपका प्रकार विकल्प होगा [T]।) मुझे लगता है कि यह उसके अनुरूप होना कठिन है।
देबिल्स्की

7

शायद यह कमांड-क्वेरी पृथक्करण सिद्धांत के कारण है?

CQS OO और कार्यात्मक प्रोग्रामिंग शैलियों के चौराहे पर लोकप्रिय होता है, क्योंकि यह ऑब्जेक्ट मेथड के बीच एक स्पष्ट अंतर पैदा करता है जो साइड-इफेक्ट्स करते हैं या नहीं होते हैं (अर्थात, ऑब्जेक्ट को बदल देते हैं)। चर असाइनमेंट में CQS को लागू करना इसे सामान्य से आगे ले जा रहा है, लेकिन एक ही विचार लागू होता है।

क्यों CQS उपयोगी है की एक छोटी चित्रण: एक साथ एक काल्पनिक संकर एफ / OO भाषा पर विचार Listवर्ग है कि तरीकों Sort, Append, First, और Length। अनिवार्य OO शैली में, कोई इस तरह से एक फ़ंक्शन लिखना चाहता है:

func foo(x):
    var list = new List(4, -2, 3, 1)
    list.Append(x)
    list.Sort()
    # list now holds a sorted, five-element list
    var smallest = list.First()
    return smallest + list.Length()

जबकि अधिक कार्यात्मक शैली में, एक व्यक्ति इस तरह से कुछ लिखने की संभावना करेगा:

func bar(x):
    var list = new List(4, -2, 3, 1)
    var smallest = list.Append(x).Sort().First()
    # list still holds an unsorted, four-element list
    return smallest + list.Length()

ये एक ही काम करने की कोशिश कर रहे हैं, लेकिन जाहिर है कि दोनों में से एक गलत है, और तरीकों के व्यवहार के बारे में अधिक जानने के बिना, हम यह नहीं बता सकते कि कौन सा है।

CQS का उपयोग करते हुए, हालांकि, हम इस बात पर जोर देंगे कि यदि Appendऔर Sortसूची में परिवर्तन किया गया है, तो उन्हें यूनिट प्रकार वापस करना होगा, इस प्रकार हमें दूसरे फॉर्म का उपयोग करके बग को पैदा करने से रोकना चाहिए जब हमें नहीं करना चाहिए। इसलिए साइड इफेक्ट की उपस्थिति भी विधि हस्ताक्षर में निहित हो जाती है।


4

मुझे लगता है कि यह कार्यक्रम / भाषा को दुष्प्रभावों से मुक्त रखने के लिए है।

आप जो वर्णन करते हैं वह एक पक्ष प्रभाव का जानबूझकर उपयोग है जो सामान्य मामले में एक बुरी चीज माना जाता है।


हे। साइड-इफेक्ट्स से मुक्त? :) इसके अलावा, तरह के मामले की कल्पना val a = b = 1(कल्पना "जादुई" valके सामने b) बनाम val a = 1; val b = 1;

इसका साइड इफेक्ट्स से कोई लेना-देना नहीं है, कम से कम यहाँ वर्णित अर्थ में नहीं है: साइड इफेक्ट (कंप्यूटर विज्ञान)
Feuermurmel

4

बूलियन अभिव्यक्ति के रूप में असाइनमेंट का उपयोग करना सबसे अच्छी शैली नहीं है। आप एक ही समय में दो चीजें करते हैं जो अक्सर त्रुटियों की ओर जाता है। और "==" के बजाय "=" के आकस्मिक उपयोग को स्केलस प्रतिबंध से बचा जाता है।


2
मुझे लगता है कि यह एक बकवास कारण है! जैसा कि ओपी ने पोस्ट किया, कोड अभी भी संकलित करता है और चलाता है: यह सिर्फ वही नहीं करता है जो आप यथोचित उम्मीद कर सकते हैं। यह एक और अधिक gotcha, कम नहीं है!
oxbow_lakes

1
यदि आप ऐसा कुछ लिखते हैं तो (a = b) यह संकलित नहीं होगा। तो कम से कम इस त्रुटि से बचा जा सकता है।
बहरीन

1
ओपी ने '==' के बजाय '=' का उपयोग नहीं किया, उन्होंने दोनों का उपयोग किया। वह उम्मीद करता है कि एक मान वापस लौटाया जा सकता है, जिसका उपयोग किसी अन्य मूल्य (उदाहरण में -1) की तुलना करने के लिए किया जा सकता है
इट्टायड

@ डायमॉन: यह संकलन करेगा (यदि जावा में कम से कम) है तो ए और बी बूलियन हैं। अगर (a = true) का उपयोग करके मैंने इस ट्रैप पर नए-नए पड़ते देखे हैं। (एक) और (और अधिक महत्वपूर्ण नाम का उपयोग करते हुए स्पष्ट अगर!) सरल पसंद करने के लिए एक और कारण।
फीलोहो

2

वैसे: मुझे शुरुआती समय के ट्रिक बेवकूफ लगते हैं, यहां तक ​​कि जावा में भी। इस तरह से क्यों नहीं?

for(int bytesRead = in.read(buffer); bytesRead != -1; bytesRead = in.read(buffer)) {
   //do something 
}

दी गई, असाइनमेंट दो बार दिखाई देता है, लेकिन कम से कम बाइट्सड्रेड उस दायरे में है जो इसका है, और मैं अजीब असाइनमेंट ट्रिक्स के साथ नहीं खेल रहा हूं ...


1
जबकि चाल एक बहुत ही आम है, यह आमतौर पर हर ऐप में दिखाई देता है जो एक बफर के माध्यम से पढ़ता है। और यह हमेशा ओपी के संस्करण की तरह दिखता है।
ट्वीस्ट्रीमोब

0

जब तक आपके पास अप्रत्यक्ष के लिए एक संदर्भ प्रकार है, तब तक आप इसके लिए वर्कअराउंड कर सकते हैं। एक भोली कार्यान्वयन में, आप मनमाने प्रकार के लिए निम्नलिखित का उपयोग कर सकते हैं।

case class Ref[T](var value: T) {
  def := (newval: => T)(pred: T => Boolean): Boolean = {
    this.value = newval
    pred(this.value)
  }
}

फिर, उस बाधा के तहत जिसे आपको ref.valueसंदर्भ को एक्सेस करने के लिए उपयोग करना होगा, आप अपने whileविधेय को इस प्रकार लिख सकते हैं

val bytesRead = Ref(0) // maybe there is a way to get rid of this line

while ((bytesRead := in.read(buffer)) (_ != -1)) { // ...
  println(bytesRead.value)
}

और आप bytesReadइसे टाइप किए बिना अधिक अंतर्निहित तरीके से चेकिंग कर सकते हैं ।

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