Erlang 21 - 6. Functions

6 कार्य




erlang

6 कार्य

6.1 पैटर्न मिलान

फंक्शन हेड के साथ-साथ case और क्लॉस receive पैटर्न को कंपाइलर द्वारा ऑप्टिमाइज़ किया जाता है। कुछ अपवादों के साथ, खंडों को पुनर्व्यवस्थित करने से कुछ हासिल नहीं होता है।

एक अपवाद बायनेरिज़ का पैटर्न मिलान है। संकलक द्विराष्ट्र से मेल खाने वाले खंडों को पुनर्व्यवस्थित नहीं करता है। खाली बाइनरी के खिलाफ मेल खाने वाले क्लॉज को रखना आमतौर पर पहले रखने की तुलना में थोड़ा तेज होता है।

निम्नलिखित एक और अपवाद दिखाने के लिए एक अप्राकृतिक उदाहरण है:

ऐसा न करें

atom_map1(one) -> 1;
atom_map1(two) -> 2;
atom_map1(three) -> 3;
atom_map1(Int) when is_integer(Int) -> Int;
atom_map1(four) -> 4;
atom_map1(five) -> 5;
atom_map1(six) -> 6.

समस्या चर Int साथ खंड है। एक चर के रूप में परमाणुओं four , five , और six सहित कुछ भी मेल कर सकता है, जो निम्न खंडों से भी मेल खाता है, संकलक को उप-कोड कोड उत्पन्न करना होगा जो निम्नानुसार निष्पादित करता है:

  • सबसे पहले, इनपुट मान की तुलना one , two और three (एक एकल निर्देश का उपयोग करके जो एक द्विआधारी खोज करता है; इस प्रकार, काफी कुशल होते हुए भी अगर कई मान हैं) जो चयन करने के लिए पहले तीन खंडों में से एक का चयन करते हैं (यदि कोई हो )।
  • यदि पहले तीन खंडों में से कोई भी मेल नहीं खाता है, तो एक खंड के रूप में चौथा खंड हमेशा मेल खाता है।
  • यदि गार्ड परीक्षण is_integer(Int) सफल होता है, तो चौथा खंड निष्पादित किया जाता है।
  • यदि गार्ड परीक्षण विफल हो जाता है, तो इनपुट मूल्य की तुलना four , five और six , और उपयुक्त क्लॉज का चयन किया जाता है। (यदि कोई भी मान मेल नहीं खाता है, तो एक function_clause अपवाद है।)

या तो पुनर्लेखन:

करना

atom_map2(one) -> 1;
atom_map2(two) -> 2;
atom_map2(three) -> 3;
atom_map2(four) -> 4;
atom_map2(five) -> 5;
atom_map2(six) -> 6;
atom_map2(Int) when is_integer(Int) -> Int.

या:

करना

atom_map3(Int) when is_integer(Int) -> Int;
atom_map3(one) -> 1;
atom_map3(two) -> 2;
atom_map3(three) -> 3;
atom_map3(four) -> 4;
atom_map3(five) -> 5;
atom_map3(six) -> 6.

थोड़ा और अधिक कुशल मिलान कोड देता है।

एक और उदाहरण:

ऐसा न करें

map_pairs1(_Map, [], Ys) ->
    Ys;
map_pairs1(_Map, Xs, [] ) ->
    Xs;
map_pairs1(Map, [X|Xs], [Y|Ys]) ->
    [Map(X, Y)|map_pairs1(Map, Xs, Ys)].

पहला तर्क कोई समस्या नहीं है। यह परिवर्तनशील है, लेकिन यह सभी खंडों में परिवर्तनशील है। समस्या मध्य तर्क में दूसरे तर्क, Xs में परिवर्तनशील है। क्योंकि चर कुछ भी मेल कर सकता है, संकलक को खंडों को फिर से व्यवस्थित करने की अनुमति नहीं है, लेकिन लिखित रूप में उनसे मेल खाने वाले कोड को उत्पन्न करना होगा।

यदि फ़ंक्शन निम्नानुसार फिर से लिखा गया है, तो संकलक खंडों को फिर से व्यवस्थित करने के लिए स्वतंत्र है:

करना

map_pairs2(_Map, [], Ys) ->
    Ys;
map_pairs2(_Map, [_|_]=Xs, [] ) ->
    Xs;
map_pairs2(Map, [X|Xs], [Y|Ys]) ->
    [Map(X, Y)|map_pairs2(Map, Xs, Ys)].

कंपाइलर इसके समान कोड उत्पन्न करेगा:

नहीं (पहले से ही संकलक द्वारा किया गया)

explicit_map_pairs(Map, Xs0, Ys0) ->
    case Xs0 of
	[X|Xs] ->
	    case Ys0 of
		[Y|Ys] ->
		    [Map(X, Y)|explicit_map_pairs(Map, Xs, Ys)];
		[] ->
		    Xs0
	    end;
	[] ->
	    Ys0
    end.

यह शायद सबसे आम मामला है कि इनपुट सूचियां खाली नहीं हैं या बहुत कम हैं। (एक अन्य लाभ यह है कि डायज़र Xs वेरिएबल के लिए बेहतर प्रकार का उपयोग कर सकता है।)

6.2 समारोह कॉल

यह अलग-अलग कॉल की सापेक्ष लागतों का जानबूझकर मोटा मार्गदर्शक है। यह Solaris / Sparc पर चलने वाले बेंचमार्क आंकड़ों पर आधारित है:

  • स्थानीय या बाहरी कार्यों ( foo() , m:foo() ) के लिए कॉल सबसे तेज़ कॉल हैं।
  • Fun() ( Fun() , apply(Fun, []) को कॉल करना या लगाना एक स्थानीय फंक्शन को कॉल करने जितना महंगा है।
  • एक निर्यात किए गए फ़ंक्शन ( Mod:Name() , apply(Mod, Name, []) एक मजेदार कॉलिंग के रूप में दो बार महंगा है या एक स्थानीय फ़ंक्शन को कॉल करने के रूप में लगभग छह गुना महंगा है।

नोट्स और कार्यान्वयन विवरण

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

apply/3 हैश तालिका में निष्पादित करने के लिए फ़ंक्शन के लिए कोड को देखना चाहिए। इसलिए यह हमेशा सीधी कॉल या मजेदार कॉल की तुलना में धीमी होती है।

यह अब मायने नहीं रखता (प्रदर्शन के दृष्टिकोण से) चाहे आप लिखें:

Module:Function(Arg1, Arg2)

या:

apply(Module, Function, [Arg1,Arg2])

कंपाइलर आंतरिक रूप से पूर्व में बाद वाले कोड को फिर से लिखता है।

निम्न कोड थोड़ा धीमा है क्योंकि तर्कों की सूची का आकार संकलन के समय अज्ञात है।

apply(Module, Function, Arguments)

रिकर्सियन में 6.3 मेमोरी उपयोग

पुनरावर्ती कार्यों को लिखते समय, उन्हें पूंछ-पुनरावर्ती बनाना बेहतर होता है ताकि वे निरंतर मेमोरी स्पेस में निष्पादित कर सकें

करना

list_length(List) ->
    list_length(List, 0).

list_length([], AccLen) -> 
    AccLen; % Base case

list_length([_|Tail], AccLen) ->
    list_length(Tail, AccLen + 1). % Tail-recursive

ऐसा न करें

list_length([]) ->
    0. % Base case
list_length([_ | Tail]) ->
    list_length(Tail) + 1. % Not tail-recursive