अधिकांश प्रोग्रामिंग भाषाओं में फ़ंक्शन घोषित करने के लिए विशेष कीवर्ड या सिंटैक्स क्यों हैं? [बन्द है]


39

अधिकांश प्रोग्रामिंग भाषाओं (दोनों गतिशील और सांख्यिकीय रूप से टाइप की गई भाषाएं) में विशेष कीवर्ड और / या सिंटैक्स होते हैं जो फ़ंक्शन घोषित करने के लिए चर घोषित करने की तुलना में बहुत अलग दिखते हैं। मैं अन्य नामांकित इकाई की घोषणा करते हुए कार्य देखता हूं:

उदाहरण के लिए पायथन:

x = 2
y = addOne(x)
def addOne(number): 
  return number + 1

क्यों नहीं:

x = 2
y = addOne(x)
addOne = (number) => 
  return number + 1

इसी तरह, जावा जैसी भाषा में:

int x = 2;
int y = addOne(x);

int addOne(int x) {
  return x + 1;
}

क्यों नहीं:

int x = 2;
int y = addOne(x);
(int => int) addOne = (x) => {
  return x + 1;
}

यह सिंटैक्स कुछ घोषित करने का अधिक स्वाभाविक तरीका है (यह एक फ़ंक्शन या एक चर है) और एक कम कीवर्ड जैसे defया functionकुछ भाषाओं में। और, IMO, यह अधिक सुसंगत है (मैं चर या फ़ंक्शन के प्रकार को समझने के लिए एक ही स्थान पर देखता हूं) और शायद लिखने के लिए पार्सर / व्याकरण को थोड़ा सरल बनाता है।

मुझे पता है कि बहुत कम भाषाएं इस विचार (कॉफीस्क्रिप्ट, हास्केल) का उपयोग करती हैं, लेकिन अधिकांश आम भाषाओं में फ़ंक्शन (जावा, सी ++, पायथन, जावास्क्रिप्ट, सी #, पीएचपी, रूबी) के लिए विशेष वाक्यविन्यास हैं।

यहां तक ​​कि स्काला में, जो दोनों तरह से समर्थन करती है (और टाइप इनफैक्शन), यह लिखना अधिक आम है:

def addOne(x: Int) = x + 1

बजाय:

val addOne = (x: Int) => x + 1

IMO, स्काला में कम से कम, यह शायद सबसे आसानी से समझ में आने वाला संस्करण है, लेकिन इस मुहावरे का शायद ही कभी पालन किया जाता है:

val x: Int = 1
val y: Int = addOne(x)
val addOne: (Int => Int) = x => x + 1

मैं अपनी खुद की टॉय लैंग्वेज पर काम कर रहा हूं और मैं सोच रहा हूं कि क्या इस तरह से मेरी भाषा डिजाइन की जाए और अगर कोई ऐतिहासिक या तकनीकी कारण हैं तो इस पैटर्न का व्यापक रूप से पालन नहीं किया जाता है?


9
कारण संभावित ऐतिहासिक है। जिसने भी ऐसा किया उसने पहले ऐसा किया और बाकी सभी ने नकल की। लेकिन मुझे संदेह है कि हम निश्चित रूप से जान सकते हैं।

29
मुझे लगता है कि यह इसलिए है क्योंकि एक फ़ंक्शन या विधि केवल एक और नामित इकाई नहीं है । कार्यात्मक भाषाओं में, वे हैं (आप उन्हें चारों ओर से पास कर सकते हैं) लेकिन अन्य भाषाओं में (जैसे जावा) एक फ़ंक्शन या विधि एक साधारण चर से पूरी तरह से अलग है और इसे इस तरह से नहीं माना जा सकता है (मैं जावा 8 को कमजोर करता हूं) कथन), इसलिए यह कार्य / विधियों को अलग तरीके से परिभाषित करने के लिए समझ में आता है, क्योंकि वे अलग तरह से व्यवहार करते हैं
11684

15
मैं असहमत हूं कि आपका प्रस्ताव कार्य घोषित करने का अधिक स्वाभाविक तरीका है। मैं वास्तव में कॉफ़्सस्क्रिप्ट के सिंटैक्स को नापसंद करता हूं और इसका हिस्सा इसके फ़ंक्शन घोषणा सिंटैक्स के साथ करना है। तथ्य यह है, अगर यह टूट नहीं है तो इसे ठीक न करें। 'डिफ' या 'फंक्शन' लिखना कोई बड़ी बात नहीं है और यह कुछ फैंसी पर्ल जैसे प्रतीकों की तुलना में अधिक स्पष्ट रूप से एक झलक है जो बिना किसी थकावट या थकी आंखों के साथ आसानी से किसी और चीज के लिए गलत हो सकता है। एक पूरी तरह से व्यक्तिगत नोट पर, मुझे भी लगता है कि उदाहरणों में आपके सुझाए गए वाक्यविन्यास वर्तमान वाक्य रचना की तुलना में बहुत अधिक बदसूरत दिखते हैं।
रॉय

22
"=> 'एक" विशेष कीवर्ड या फ़ंक्शन घोषित करने के लिए वाक्यविन्यास "कैसे नहीं है?
TML

11
मैं तुम्हारे बारे में नहीं जानता, लेकिन मेरे (int => int) addOne = (x) => {लिए "विशेष" और "जटिल" की तुलना में बहुत अधिक है int addOne(int) {...
बोगडान अलेक्जेंड्रू

जवाबों:


48

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

और इन भाषाओं में, फ़ंक्शन केवल एक और मूल्य नहीं हैं :

  • C ++, C # और Java में, आप फ़ंक्शन को ओवरलोड कर सकते हैं: आपके पास एक ही नाम के साथ दो कार्य हो सकते हैं, लेकिन अलग-अलग हस्ताक्षर।
  • C, C ++, C # और Java में, आपके पास ऐसे मान हो सकते हैं जो फ़ंक्शन का प्रतिनिधित्व करते हैं, लेकिन फ़ंक्शन पॉइंटर्स, फ़ंक्शनलर्स, डेलिगेट्स और फ़ंक्शनल इंटरफेस सभी स्वयं फ़ंक्शन से अलग हैं। इसका कारण यह है कि उनमें से अधिकांश वास्तव में केवल कार्य नहीं हैं, वे कुछ (उत्परिवर्तित) अवस्था के साथ मिलकर कार्य करते हैं।
  • चर डिफ़ॉल्ट रूप से उत्परिवर्तनीय होते हैं (आपको म्यूटेशन का उपयोग करना होगा const, readonlyया finalम्यूटेशन करना होगा), लेकिन फ़ंक्शंस को पुन: असाइन नहीं किया जा सकता है।
  • अधिक तकनीकी दृष्टिकोण से, कोड (जो कार्यों से बना है) और डेटा अलग हैं। वे आम तौर पर स्मृति के विभिन्न हिस्सों पर कब्जा कर लेते हैं, और उन्हें अलग तरीके से एक्सेस किया जाता है: कोड को एक बार लोड किया जाता है और फिर केवल निष्पादित (लेकिन पढ़ा या लिखित नहीं) किया जाता है, जबकि डेटा को अक्सर आवंटित किया जाता है और निपटाया जाता है और लिखा और पढ़ा जा रहा है, लेकिन कभी भी निष्पादित नहीं किया जाता है।

    और चूंकि सी का अर्थ "धातु के करीब" होना था, इसलिए यह इस भेद को भाषा के वाक्य-विन्यास में भी प्रतिबिंबित करता है।

  • "फ़ंक्शन सिर्फ एक मूल्य है" दृष्टिकोण जो कार्यात्मक प्रोग्रामिंग का आधार बनता है, सामान्य भाषाओं में केवल अपेक्षाकृत हाल ही में कर्षण प्राप्त हुआ है, जैसा कि C ++, C # और जावा (2011, 2007, 2014) में लैम्ब्डा के देर से परिचय से स्पष्ट है।


12
सी # के अनाम कार्य थे - जो कि बिना किसी प्रकार के अंतर्विरोध के लैंबडास और 2005 के बाद से एक स्पष्ट वाक्यविन्यास हैं।
एरिक लिपर्ट

2
"एक अधिक तकनीकी परिप्रेक्ष्य से, कोड (जो कार्यों से बना है) और डेटा अलग हैं" - कुछ भाषाएं, सबसे अच्छी तरह से जानबूझकर LISPs उस अलगाव को नहीं बनाते हैं और वास्तव में कोड और डेटा को बहुत अलग तरीके से व्यवहार नहीं करते हैं। (LISPs सबसे अच्छी तरह से ज्ञात उदाहरण हैं, लेकिन REBOL जैसी अन्य भाषाओं का एक समूह है जो ऐसा करते हैं)
बेंजामिन ग्रुएनबाउम

1
@BenjaminGruenbaum मैं भाषा के स्तर के बारे में नहीं, बल्कि स्मृति में संकलित कोड के बारे में बात कर रहा था।
12

C में फंक्शन पॉइंटर्स होते हैं, जिन्हें कम से कम थोड़ा, केवल एक और मान माना जा सकता है। सुनिश्चित करने के लिए गड़बड़ करने के लिए एक खतरनाक मूल्य, लेकिन अजनबी चीजें हुई हैं।
पैट्रिक ह्यूजेस

@PatrickHughes हाँ, मैं अपने दूसरे बिंदु में उनका उल्लेख करता हूँ। लेकिन फ़ंक्शन पॉइंटर्स फ़ंक्शन से काफी अलग हैं।
svick

59

ऐसा इसलिए है क्योंकि मनुष्यों के लिए यह समझना महत्वपूर्ण है कि कार्य केवल "एक अन्य नामित संस्था" नहीं हैं। कभी-कभी यह उन्हें इस तरह से हेरफेर करने के लिए समझ में आता है, लेकिन वे अभी भी एक नज़र में पहचाने जाने में सक्षम हैं।

यह वास्तव में मायने नहीं रखता है कि कंप्यूटर सिंटैक्स के बारे में क्या सोचता है, क्योंकि वर्णों का एक समझदार बूँद एक मशीन के लिए व्याख्या करने के लिए ठीक है, लेकिन यह मनुष्य के लिए समझने और बनाए रखने के लिए शून्य-असंभव होगा।

यह वास्तव में एक ही कारण है कि हमारे पास और छोरों के लिए क्यों है, स्विच करें और यदि आवश्यक हो, आदि, भले ही वे सभी अंततः एए तुलना करने और निर्देश को कूदने के लिए नीचे उबालें। कारण यह है कि यह कोड को बनाए रखने और समझने वाले मनुष्यों के लाभ के लिए है।

अपने कार्यों को "एक अन्य नामित संस्था" के रूप में करने से आप जिस तरह से प्रस्ताव कर रहे हैं वह आपके कोड को देखने के लिए कठिन बना देगा, और इस प्रकार समझना कठिन होगा।


12
मैं असहमत हूं कि नामित संस्थाओं के रूप में कार्यों को समझना जरूरी कोड को समझने में कठिन बनाता है। यह किसी भी कोड के लिए लगभग निश्चित रूप से सच है जो एक प्रक्रियागत प्रतिमान का अनुसरण करता है, लेकिन यह उस कोड के लिए सही नहीं हो सकता है जो एक कार्यात्मक प्रतिमान का अनुसरण करता है।
काइल स्ट्रैंड

29
"यह वास्तव में वही कारण है जो हमारे पास है और छोरों के लिए है, स्विच करें और यदि आवश्यक हो, आदि, भले ही वे सभी अंततः एए तुलना और कूदने के निर्देश को उबालें। " यदि सभी प्रोग्रामर मशीन भाषा के साथ सहज थे, तो हमें इन सभी फैंसी प्रोग्रामिंग भाषाओं (उच्च या निम्न स्तर) की आवश्यकता नहीं होगी। लेकिन सच्चाई यह है: हर किसी का एक अलग आराम स्तर होता है कि वे अपने कोड को लिखने के लिए मशीन के कितने करीब होते हैं। एक बार जब आप अपना स्तर खोज लेते हैं, तो आपको बस आपको दिए गए वाक्यविन्यास के साथ रहना होगा (या यदि आप वास्तव में परेशान हैं तो अपनी खुद की भाषा चश्मा और संकलक लिखें)।
होकी

12
+1। एक अधिक संक्षिप्त संस्करण हो सकता है "सभी प्राकृतिक भाषाएं क्रियाओं से संज्ञाओं को अलग क्यों करती हैं?"
msw

2
"वर्णों का एक समझदार बूँद व्याख्या करने के लिए एक मशीन के लिए ठीक है, लेकिन यह मनुष्यों के लिए समझने और बनाए रखने के लिए शून्य-असंभव होगा" प्रश्न में प्रस्तावित ऐसे मामूली वाक्यविन्यास परिवर्तन के लिए एक दाने की योग्यता क्या है। एक जल्दी से वाक्य रचना के लिए adapts। यह प्रस्ताव OO विधि कॉल सिंटैक्स ऑब्जेक्ट.method (args) की तुलना में कन्वेंशन से बहुत कम कट्टरपंथी प्रस्थान है , फिर भी उत्तरार्द्ध मनुष्य के लिए समझने और बनाए रखने के लिए शून्य-असंभव साबित नहीं हुआ है। इस तथ्य के बावजूद कि ज्यादातर OO भाषाओं के तरीकों को वर्ग उदाहरणों में संग्रहीत के रूप में नहीं सोचा जाना चाहिए।
मार्क वैन लीउवेन

4
@MarcvanLeeuwen। आपकी टिप्पणी सच है और समझ में आती है, लेकिन मूल पोस्ट को फिर से भेजने के तरीके से कुछ भ्रम की स्थिति पैदा होती है। वास्तव में 2 प्रश्न हैं: (i) ऐसा क्यों है? और (ii) इस तरह से क्यों नहीं? । पहले उत्तर whatsisnameको पहले बिंदु पर अधिक संबोधित किया गया था (और इन विफलताओं को हटाने के कुछ खतरे पर चेतावनी दी गई थी), जबकि आपकी टिप्पणी प्रश्न के दूसरे भाग से अधिक संबंधित है। यह वास्तव में इस वाक्यविन्यास को बदलना संभव है (और जैसा कि आपने वर्णित किया है, यह पहले से ही कई बार किया गया है ...), लेकिन यह हर किसी के अनुरूप नहीं होगा (जैसा कि ऊप हर किसी के अनुरूप नहीं होता है।
होकी

10

आपको यह जानने में रुचि हो सकती है कि, प्रागैतिहासिक काल में वापस जाने के लिए, ALGOL 68 नामक एक भाषा ने एक सिंटैक्स का उपयोग किया था जो आप प्रस्तावित करते हैं। यह पहचानना कि फ़ंक्शन आइडेंटिफ़ायर अन्य आइडेंटिफ़ायर की तरह ही मूल्यों के लिए बाध्य हैं, आप उस भाषा में सिंटैक्स का उपयोग करके फ़ंक्शन (स्थिर) घोषित कर सकते हैं

फ़ंक्शन-प्रकार का नाम = ( पैरामीटर-सूची ) परिणाम-प्रकार : शरीर ;

अपने उदाहरण पढ़ेगा

PROC (INT)INT add one = (INT n) INT: n+1;

उस अतिरेक को स्वीकार करते हुए कि प्रारंभिक प्रकार को घोषणा के आरएचएस से पढ़ा जा सकता है, और एक फ़ंक्शन प्रकार हमेशा से शुरू होता है PROC, यह (और आमतौर पर) अनुबंधित किया जाएगा

PROC add one = (INT n) INT: n+1;

लेकिन ध्यान दें कि =अभी भी पैरामीटर सूची से पहले आता है । यह भी ध्यान दें कि यदि आप एक फंक्शन वेरिएबल चाहते हैं (जिस पर उसी फंक्शन टाइप का एक और वैल्यू बाद में असाइन किया जा सके) , को या तो एक से =बदल दिया जाना चाहिए :=

PROC (INT)INT func var := (INT n) INT: n+1;
PROC func var := (INT n) INT: n+1;

हालाँकि इस मामले में दोनों रूप वास्तव में संक्षिप्त हैं; चूंकि पहचानकर्ता func varस्थानीय रूप से उत्पन्न फ़ंक्शन के संदर्भ को निर्दिष्ट करता है, इसलिए पूरी तरह से विस्तारित रूप होगा

REF PROC (INT)INT func var = LOC PROC (INT)INT := (INT n) INT: n+1;

यह विशेष रूप से वाक्य रचना का उपयोग करने के लिए आसान है, लेकिन यह स्पष्ट रूप से अन्य प्रोग्रामिंग भाषाओं में बड़ा नहीं था। हास्केल जैसी कार्यात्मक प्रोग्रामिंग भाषाएं भी पैरामीटर सूची का पालन करने के f n = n+1साथ शैली को पसंद करती हैं । मुझे लगता है कि इसका कारण मुख्य रूप से मनोवैज्ञानिक है; के बाद सभी भी गणितज्ञों अक्सर पसंद नहीं करते, मैं करता हूँ के रूप में, = nn + 1 से अधिक ( एन ) = n + 1।=

वैसे, उपरोक्त चर्चा चर और कार्यों के बीच एक महत्वपूर्ण अंतर को उजागर करती है: फ़ंक्शन परिभाषाएं आमतौर पर एक विशिष्ट फ़ंक्शन मान के लिए एक नाम को बांधती हैं, जिसे बाद में नहीं बदला जा सकता है, जबकि चर परिभाषाएं आमतौर पर एक प्रारंभिक मान के साथ एक पहचानकर्ता का परिचय देती हैं , लेकिन एक बाद में बदल सकते हैं। (यह एक पूर्ण नियम नहीं है; फ़ंक्शन चर और गैर-फ़ंक्शन स्थिरांक अधिकांश भाषाओं में होते हैं।) इसके अलावा, संकलित भाषाओं में फ़ंक्शन परिभाषा में बंधे मूल्य आमतौर पर एक संकलन-समय स्थिर होता है, ताकि फ़ंक्शन को कॉल किया जा सके। कोड में एक निश्चित पते का उपयोग करके संकलित किया गया। C / C ++ में यह एक आवश्यकता है; ALGOL 68 के बराबर

PROC (REAL) REAL f = IF mood=sunny THEN sin ELSE cos FI;

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


1
"गणितज्ञों को f = n hem n + 1 ओवर f ( n ) = n + 1" पसंद नहीं है ... भौतिकविदों का उल्लेख नहीं करने के लिए, जो केवल f = n + 1 लिखना पसंद करते हैं ...
leftaroundabout

1
@leftaroundabout ऐसा इसलिए है क्योंकि। लिखने के लिए एक दर्द है। ईमानदारी से।
पियरे अरलाउड

8

आपने उदाहरण के रूप में जावा और स्काला का उल्लेख किया है। हालाँकि, आपने एक महत्वपूर्ण तथ्य की अनदेखी की: वे कार्य नहीं हैं, वे विधियाँ हैं। विधियाँ और कार्य मौलिक रूप से भिन्न होते हैं। कार्य वस्तुएं हैं, विधियां वस्तुओं से संबंधित हैं।

स्काला में, जिसके कार्य और विधियाँ दोनों हैं, विधियों और कार्यों के बीच निम्न अंतर हैं:

  • तरीके सामान्य हो सकते हैं, कार्य नहीं कर सकते
  • विधियों में कोई, एक या कई पैरामीटर सूची नहीं हो सकती हैं, फ़ंक्शंस में हमेशा एक पैरामीटर सूची होती है
  • विधियों में एक अंतर्निहित पैरामीटर सूची हो सकती है, फ़ंक्शन नहीं हो सकते
  • विधियों में डिफ़ॉल्ट तर्कों के साथ वैकल्पिक पैरामीटर हो सकते हैं, फ़ंक्शंस नहीं हो सकते
  • तरीकों में दोहराए जाने वाले पैरामीटर हो सकते हैं, फ़ंक्शन नहीं कर सकते
  • विधियों के नाम पैरामीटर हो सकते हैं, फ़ंक्शंस नहीं कर सकते
  • विधियों को नामित तर्कों के साथ बुलाया जा सकता है, फ़ंक्शन नहीं कर सकते
  • फ़ंक्शन ऑब्जेक्ट हैं, विधियाँ नहीं हैं

तो, आपका प्रस्तावित प्रतिस्थापन केवल काम नहीं करता है, कम से कम उन मामलों के लिए।


2
"फ़ंक्शंस ऑब्जेक्ट हैं, विधियाँ ऑब्जेक्ट्स की हैं।" क्या यह सभी भाषाओं पर लागू होना चाहिए (जैसे C ++)?
svick

9
"विधियों के नाम पैरामीटर हो सकते हैं, फ़ंक्शंस नहीं कर सकते हैं" - यह तरीकों और कार्यों के बीच एक बुनियादी अंतर नहीं है, यह स्काला का सिर्फ एक क्विक है। दूसरों के सबसे (सभी?) के साथ भी।
user253751

1
विधियाँ मौलिक रूप से केवल एक विशेष प्रकार के कार्य हैं। -1। ध्यान दें कि जावा (और विस्तार स्काला द्वारा) किसी भी अन्य प्रकार के कार्य नहीं करता है। यह "फ़ंक्शन" एक विधि के साथ ऑब्जेक्ट हैं (सामान्य कार्यों में ऑब्जेक्ट या यहां तक ​​कि प्रथम श्रेणी के मूल्य नहीं हो सकते हैं; चाहे वे भाषा पर निर्भर हों)।
जान हुदेक

@ जानहुडेक: शब्दार्थ, जावा में कार्य और विधियाँ दोनों हैं; यह फ़ंक्शन का उल्लेख करते समय शब्दावली "स्थिर तरीकों" का उपयोग करता है, लेकिन ऐसी शब्दावली अर्थ भेद को मिटाती नहीं है।
सुपरकैट

3

कारण जो मैं सोच सकता हूं:

  • कंपाइलर के लिए यह जानना आसान है कि हम क्या घोषित कर रहे हैं।
  • हमारे लिए यह जानना महत्वपूर्ण है (एक तुच्छ तरीके से) कि क्या यह एक फ़ंक्शन या एक चर है। कार्य आमतौर पर ब्लैक बॉक्स होते हैं और हम उनके आंतरिक कार्यान्वयन के बारे में परवाह नहीं करते हैं। मुझे स्काला में रिटर्न टाइप्स पर टाइप इंट्रेंस पसंद नहीं है, क्योंकि मेरा मानना ​​है कि किसी फंक्शन का इस्तेमाल करना ज्यादा आसान है, जिसमें रिटर्न टाइप होता है: यह अक्सर केवल डॉक्यूमेंट दिया जाता है।
  • और सबसे महत्वपूर्ण एक निम्नलिखित रणनीति है जिसे प्रोग्रामिंग भाषाओं को डिजाइन करने में उपयोग किया जाता है। C ++ को C प्रोग्रामर्स को चोरी करने के लिए बनाया गया था, और जावा को इस तरह से डिज़ाइन किया गया था कि C ++ प्रोग्रामर को डराए नहीं, और Java प्रोग्रामर को आकर्षित करने के लिए C #। यहां तक ​​कि सी #, जो मुझे लगता है कि एक बहुत ही आधुनिक भाषा है, इसके पीछे एक अद्भुत टीम है, जावा से कुछ गलतियों की नकल की या सी से भी।

2
"... और जावा प्रोग्रामर को आकर्षित करने के लिए C #" - यह संदिग्ध है, C # जावा की तुलना में C ++ के समान अधिक है। और कभी-कभी ऐसा लगता है कि इसे जानबूझकर जावा (कोई वाइल्डकार्ड, कोई आंतरिक वर्ग, कोई वापसी प्रकार सहसंयोजक नहीं) के साथ असंगत बना दिया गया था
सर्ज बोर्स्च

1
@SargeBorsch मुझे नहीं लगता कि इसे जानबूझकर जावा के साथ असंगत बनाया गया था, मुझे पूरा यकीन है कि C # टीम ने इसे सही तरीके से करने की कोशिश की, या इसे बेहतर किया, लेकिन आप इसे देखना चाहते हैं। Microsoft ने पहले से ही अपने स्वयं के जावा और JVM को लिखा और मुकदमा किया। इसलिए C # टीम जावा को ग्रहण करने के लिए बहुत प्रेरित थी । यह निश्चित रूप से असंगत है, और इसके लिए बेहतर है। जावा ने कुछ बुनियादी सीमाओं के साथ शुरुआत की और मुझे खुशी है कि सी # टीम ने उदाहरण के लिए, जेनरिक को अलग तरह से करने के लिए चुना (प्रकार प्रणाली में और न केवल चीनी)।
कोडेनहेम

2

प्रश्न को चारों ओर घुमाते हुए, यदि कोई मशीन पर स्रोत कोड को संपादित करने की कोशिश करने में दिलचस्पी नहीं रखता है जो बेहद रैम-विवश है, या इसे एक फ्लॉपी डिस्क से पढ़ने का समय कम से कम है, तो कीवर्ड का उपयोग करने में क्या गलत है?

निश्चित रूप से यह पढ़ने की x=y+zतुलना में अच्छा है store the value of y plus z into x, लेकिन इसका मतलब यह नहीं है कि विराम चिह्नों को खोजशब्दों की तुलना में स्वाभाविक रूप से "बेहतर" माना जाता है। यदि चर i, jऔर kहैं Integer, और xहै Real, तो पास्कल में निम्नलिखित पंक्तियों पर विचार करें:

k := i div j;
x := i/j;

पहली पंक्ति एक पूर्णांक विभाजन विभाजन का प्रदर्शन करेगी, जबकि दूसरी वास्तविक संख्या विभाजन का प्रदर्शन करेगी। अंतर को अच्छी तरह से बनाया जा सकता है क्योंकि पास्कल divअपने ट्रंकटिंग-पूर्णांक-डिवीजन ऑपरेटर के रूप में उपयोग करता है , बजाय एक विराम चिह्न का उपयोग करने की कोशिश करने के जो पहले से ही एक अन्य उद्देश्य (वास्तविक-संख्या विभाजन) है।

हालांकि कुछ संदर्भ हैं, जिनमें फ़ंक्शन परिभाषा को संक्षिप्त बनाने में मदद मिल सकती है (उदाहरण के लिए एक लैम्ब्डा जो कि किसी अन्य अभिव्यक्ति के भाग के रूप में उपयोग किया जाता है), कार्यों को आमतौर पर बाहर खड़े होने और कार्यों के रूप में आसानी से पहचाने जाने योग्य माना जाता है। हालांकि यह संभव हो सकता है कि भेद को और अधिक सूक्ष्म बना दिया जाए और विराम चिह्नों के अलावा कुछ भी उपयोग न किया जाए, फिर क्या होगा? कहने Function Foo(A,B: Integer; C: Real): Stringसे यह स्पष्ट होता है कि फ़ंक्शन का नाम क्या है, यह किन मापदंडों की अपेक्षा करता है, और यह क्या देता है। हो सकता है कि Functionकुछ विराम चिह्नों के स्थान पर कोई छह या सात अक्षरों से इसे छोटा कर सकता है , लेकिन इससे क्या हासिल होगा?

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


3
मुझे नहीं लगता कि यह प्रश्न संक्षिप्तता के बारे में है। विशेष रूप से उदाहरण के लिए void f() {}, वास्तव में C ++ ( auto f = [](){};), C # ( Action f = () => {};) और जावा ( Runnable f = () -> {};) में बराबर के लंबो से कम है । लैम्ब्डास की संक्षिप्तता प्रकार की धारणा और छोड़ने से आती है return, लेकिन मुझे नहीं लगता कि यह सवाल क्या है, इससे संबंधित है।
स्विक

2

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

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


1

व्यक्तिगत रूप से, मुझे आपके विचार में कोई घातक दोष दिखाई नहीं देता है; आप पा सकते हैं कि यह आपके नए वाक्यविन्यास का उपयोग करके कुछ चीजों को व्यक्त करने की अपेक्षा से अधिक पेचीदा है, और / या आप पा सकते हैं कि आपको इसे संशोधित करने की आवश्यकता है (विभिन्न विशेष मामलों और अन्य विशेषताओं आदि को जोड़ते हुए), लेकिन मुझे संदेह है कि आप खुद को पाएंगे। विचार को पूरी तरह से त्यागने की जरूरत है।

आपके द्वारा प्रस्तावित वाक्यविन्यास गणित में कार्यों या प्रकारों को व्यक्त करने के लिए उपयोग किए जाने वाले कुछ संकेतन शैलियों के एक संस्करण की तरह कम या ज्यादा दिखता है। इसका मतलब यह है कि, सभी व्याकरणों की तरह, यह शायद कुछ प्रोग्रामर को दूसरों की तुलना में अधिक अपील करेगा। (एक गणितज्ञ के रूप में, मुझे यह पसंद है।)

हालाँकि, आपको ध्यान देना चाहिए कि अधिकांश भाषाओं में, def-स्टाइल सिंटैक्स (यानी पारंपरिक वाक्यविन्यास) एक मानक चर असाइनमेंट से अलग व्यवहार करता है

  • में Cऔर C++परिवार में, कार्यों को आम तौर पर "ऑब्जेक्ट्स" के रूप में नहीं माना जाता है, अर्थात टाइप किए गए डेटा के विखंडू को कॉपी किया जाता है और स्टैक और व्हाट्सन पर रखा जाता है। (हां, आपके पास फ़ंक्शन पॉइंटर्स हो सकते हैं, लेकिन वे अभी भी निष्पादन योग्य कोड पर इंगित करते हैं, विशिष्ट अर्थ में "डेटा" पर नहीं।)
  • अधिकांश OO भाषाओं में, विधियों (यानी सदस्य कार्यों) के लिए विशेष हैंडलिंग है; यही है, वे एक वर्ग परिभाषा के दायरे के भीतर घोषित किए गए कार्य नहीं हैं। सबसे महत्वपूर्ण अंतर यह है कि जिस ऑब्जेक्ट को विधि कहा जा रहा है वह आमतौर पर विधि के लिए एक अंतर्निहित पहले पैरामीटर के रूप में पारित किया जाता है। पायथन इसे स्पष्ट करता है self(जो, वैसे, वास्तव में एक कीवर्ड नहीं है; आप किसी भी मान्य पहचानकर्ता को किसी विधि का पहला तर्क बना सकते हैं)।

आपको यह विचार करने की आवश्यकता है कि क्या आपका नया वाक्यविन्यास (और उम्मीद है कि सहज रूप से) यह दर्शाता है कि संकलक या दुभाषिया वास्तव में क्या कर रहा है। रूबी में लैम्ब्डा और विधियों के बीच अंतर को कहने में मदद मिल सकती है; इससे आपको अंदाजा हो जाएगा कि कैसे आपके कार्य-समान-डेटा प्रतिमान विशिष्ट OO / प्रक्रियात्मक प्रतिमान से भिन्न होते हैं।


1

कुछ भाषाओं के लिए फ़ंक्शन मान नहीं हैं। ऐसा कहने के लिए ऐसी भाषा में

val f = << i : int  ... >> ;

एक फ़ंक्शन परिभाषा है, जबकि

val a = 1 ;

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

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

यदि, आपकी भाषा में, फ़ंक्शंस 1st क्लास हैं, तो फिर एक सिंटैक्स खोजने की कोशिश क्यों न करें जो पर्याप्त संक्षिप्त है कि आप एक सिंटैक्टिक चीनी खोजने के लिए परीक्षा नहीं करेंगे?

- संपादित करें -

एक और मुद्दा जिसे कोई नहीं लाया है (अभी तक) पुनरावृत्ति है। यदि आपकी अनुमति हो

{ 
    val f = << i : int ... g(i-1) ... >> ;
    val g = << j : int ... f(i-1) ... >> ;
    f(x)
}

और आप अनुमति देते हैं

{
    val a = 42 ;
    val b = a + 1 ;
    a
} ,

क्या यह अनुसरण करता है कि आपको अनुमति देनी चाहिए

{
    val a = b + 1 ; 
    val b = a - 1 ;
    a
} ?

एक आलसी भाषा में (हस्केल की तरह), यहां कोई मुद्दा नहीं है। अनिवार्य रूप से कोई स्थिर चेक (जैसे LISP) वाली भाषा में, यहाँ कोई समस्या नहीं है। लेकिन एक सांख्यिकीय रूप से जाँच की गई उत्सुक भाषा में, आपको इस बात से सावधान रहना होगा कि स्थैतिक जाँच के नियमों को कैसे परिभाषित किया जाए, यदि आप पहले दो को अनुमति देना चाहते हैं और अंतिम को मना करते हैं।

- संपादन का अंत -

* यह तर्क दिया जा सकता है कि हास्केल इस सूची में नहीं हैं। यह एक फ़ंक्शन को घोषित करने के दो तरीके प्रदान करता है, लेकिन दोनों एक प्रकार से, दूसरे प्रकार के स्थिरांक घोषित करने के लिए वाक्य रचना के सामान्यीकरण हैं


0

यह गतिशील भाषाओं पर उपयोगी हो सकता है जहां प्रकार उतना महत्वपूर्ण नहीं है, लेकिन यह स्थिर टाइप की गई भाषाओं में पढ़ने योग्य नहीं है जहां आप हमेशा अपने चर के प्रकार को जानना चाहते हैं। इसके अलावा, ऑब्जेक्ट-ओरिएंटेड भाषाओं में यह आपके चर के प्रकार को जानने के लिए बहुत महत्वपूर्ण है, ताकि यह पता चल सके कि यह किस ऑपरेशन का समर्थन करता है।

आपके मामले में, 4 चर वाला एक समारोह होगा:

(int, long, double, String => int) addOne = (x, y, z, s) => {
  return x + 1;
}

जब मैं फ़ंक्शन हेडर को देखता हूं और देखता हूं (x, y, z, s) लेकिन मुझे इन चर के प्रकारों का पता नहीं है। यदि मैं जानना चाहता हूं कि zकिस प्रकार का तीसरा पैरामीटर है, तो मुझे फ़ंक्शन की शुरुआत को देखना होगा और 1, 2, 3 की गणना करना शुरू करना होगा और फिर यह देखना होगा कि प्रकार क्या है double। पूर्व में मैं सीधे देखता हूं और देखता हूं double z


3
बेशक उस अद्भुत चीज़ को टाइप इंफ़ेक्शन कहा जाता है, जो आपको var addOne = (int x, long y, double z, String s) => { x + 1 }अपनी पसंद की गैर-नैतिक रूप से टाइप की गई भाषा में कुछ लिखने की अनुमति देती है (उदाहरण: C #, C ++, Scala)। यहां तक ​​कि अन्यथा सी # द्वारा उपयोग किए जाने वाले बहुत सीमित स्थानीय प्रकार के इंजेक्शन इसके लिए पूरी तरह से पर्याप्त हैं। तो यह जवाब केवल एक विशिष्ट वाक्यविन्यास की आलोचना कर रहा है जो पहली जगह में संदिग्ध है, और वास्तव में कहीं भी उपयोग नहीं किया जाता है (हालांकि हास्केल के वाक्यविन्यास में एक समान समस्या है)।
अमन

4
@amon उनका उत्तर वाक्य रचना की आलोचना कर सकता है, लेकिन यह भी बताता है कि प्रश्न में दिए गए वाक्यविन्यास सभी के लिए "स्वाभाविक" नहीं हो सकते हैं।

1
@amon प्रकार के अनुमान की उपयोगिता हर किसी के लिए एक अद्भुत बात नहीं है। यदि आप कोड को अधिक बार पढ़ते हैं कि आप कोड लिखते हैं, तो टाइप इनफ़ॉर्मेशन खराब है क्योंकि आपको कोड पढ़ते समय अपने सिर में लिखना होगा; और जिस तरह से उपयोग किया जाता है उसके बारे में वास्तव में सोचने की तुलना में टाइप को पढ़ने के लिए और अधिक तेज़ है।
m3th0dman

1
आलोचना मुझे मान्य लगती है। जावा के लिए, ओपी को लगता है कि [इनपुट, आउटपुट, नाम, इनपुट] एक सरल / स्पष्ट फ़ंक्शन है जो केवल [आउटपुट, नाम, इनपुट] की तुलना में है? @ M3th0dman के अनुसार, लंबे हस्ताक्षर के साथ, ओपी का 'क्लीनर' दृष्टिकोण बहुत लंबा हो जाता है।
माइकल

0

अधिकांश भाषाओं में इस तरह का अंतर होने का एक बहुत ही सरल कारण है: मूल्यांकन और घोषणा में अंतर करने की आवश्यकता है । आपका उदाहरण अच्छा है: चरों की तरह क्यों नहीं? खैर, चर अभिव्यक्तियों का तुरंत मूल्यांकन किया जाता है।

हास्केल का एक विशेष मॉडल है जहां मूल्यांकन और घोषणा के बीच कोई अंतर नहीं है, यही वजह है कि एक विशेष कीवर्ड की कोई आवश्यकता नहीं है।


2
लेकिन लैम्ब्डा कार्यों के लिए कुछ सिंटैक्स भी इसे हल करता है, इसलिए इसका उपयोग क्यों नहीं किया जाता है?
21

0

अधिकांश भाषाओं में कार्यात्मक को शाब्दिक, वस्तुओं आदि से अलग-अलग घोषित किया जाता है क्योंकि उनका उपयोग अलग-अलग तरीके से किया जाता है, अलग-अलग तरीके से डिबग किया जाता है, और त्रुटि के विभिन्न संभावित स्रोतों को रोका जाता है।

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

जावा में कुछ प्रकार के कर्नेल मॉड्यूल को डिबग करने पर विचार करें, जहां प्रत्येक ऑब्जेक्ट में एक स्ट्रिंग () संचालन होता है। हालांकि यह उम्मीद की जा सकती है कि स्ट्रींग () विधि को ऑब्जेक्ट को पुनर्स्थापित करना चाहिए, इसके लिए स्टिंग ऑब्जेक्ट को इसके मूल्य का अनुवाद करने के लिए ऑब्जेक्ट को अलग करना और पुन: इकट्ठा करना पड़ सकता है। यदि आप उन तरीकों को डीबग करने का प्रयास कर रहे हैं जो अपने काम को करने के लिए (एक हुक-और-टेम्पलेट परिदृश्य में) toString () कॉल कर रहे हैं, और गलती से अधिकांश IDE के चर विंडो में ऑब्जेक्ट को हाइलाइट करेंगे, तो यह डीबगर को क्रैश कर सकता है। ऐसा इसलिए है क्योंकि आईडीई उस ऑब्जेक्ट को स्ट्रेचिंग () करने की कोशिश करेगा जो उस कोड को कॉल करता है जिसे आप डीबग करने की प्रक्रिया में हैं। कोई भी आदिम मूल्य कभी भी इस तरह से बकवास नहीं करता है क्योंकि आदिम मूल्यों का अर्थ अर्थ भाषा द्वारा परिभाषित किया जाता है, न कि प्रोग्रामर द्वारा।

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