Erlang 21 - 2. Funs

2 फं




erlang

2 फं

2.1 नक्शा

निम्न फ़ंक्शन, सूची में प्रत्येक तत्व को double , दोगुना करता है:

double([H|T]) -> [2*H|double(T)];
double([])    -> [].

इसलिए, इनपुट के रूप में दर्ज किया गया तर्क निम्नानुसार है:

> double([1,2,3,4]).
[2,4,6,8]

निम्न फ़ंक्शन, add_one , सूची में प्रत्येक तत्व में एक जोड़ता है:

add_one([H|T]) -> [H+1|add_one(T)];
add_one([])    -> [].

फ़ंक्शन double और add_one में एक समान संरचना है। इसका उपयोग एक फ़ंक्शन map लिखकर किया जा सकता है जो इस समानता को व्यक्त करता है:

map(F, [H|T]) -> [F(H)|map(F, T)];
map(F, [])    -> [].

कार्य double और add_one अब map रूप में निम्नानुसार व्यक्त किए जा सकते हैं:

double(L)  -> map(fun(X) -> 2*X end, L).
add_one(L) -> map(fun(X) -> 1 + X end, L).

map(F, List) एक ऐसा कार्य है जो एक फ़ंक्शन F और एक सूची L को तर्क के रूप में लेता है और एक नई सूची देता है, L में प्रत्येक तत्व के लिए F को लागू करके प्राप्त किया जाता है।

कई विभिन्न कार्यक्रमों की सामान्य विशेषताओं को अमूर्त करने की प्रक्रिया को प्रक्रियात्मक अमूर्तता कहा जाता है । प्रक्रियात्मक अमूर्तता का उपयोग कई अलग-अलग कार्यों को लिखने के लिए किया जा सकता है जिनकी संरचना समान होती है, लेकिन कुछ मामूली विस्तार से भिन्न होती है। यह अग्रानुसार होगा:

  • चरण 1. एक फ़ंक्शन लिखें जो इन कार्यों की सामान्य विशेषताओं का प्रतिनिधित्व करता है।
  • चरण 2. सामान्य फ़ंक्शन के तर्क के रूप में पारित किए गए फ़ंक्शंस के संदर्भ में अंतर को परिमाणित करें।

२.२ फाटक

यह खंड प्रक्रियात्मक अमूर्तता को दर्शाता है। प्रारंभ में, निम्नलिखित दो उदाहरण पारंपरिक कार्यों के रूप में लिखे गए हैं।

यह फ़ंक्शन किसी सूची के सभी तत्वों को एक स्ट्रीम पर प्रिंट करता है:

print_list(Stream, [H|T]) ->
    io:format(Stream, "~p~n", [H]),
    print_list(Stream, T);
print_list(Stream, []) ->
    true.

यह फ़ंक्शन किसी संदेश को प्रक्रियाओं की सूची में प्रसारित करता है:

broadcast(Msg, [Pid|Pids]) ->
    Pid ! Msg,
    broadcast(Msg, Pids);
broadcast(_, []) ->
    true.

इन दो कार्यों में एक समान संरचना है। वे दोनों एक सूची में पुनरावृति करते हैं और सूची में प्रत्येक तत्व के लिए कुछ करते हैं। "कुछ" फ़ंक्शन के अतिरिक्त तर्क के रूप में पारित किया जाता है जो ऐसा करता है।

फ़ंक्शन foreach इस समानता को व्यक्त करता है:

foreach(F, [H|T]) ->
    F(H),
    foreach(F, T);
foreach(F, []) ->
    ok.

फ़ंक्शन foreach का उपयोग करते हुए, फ़ंक्शन print_list बन जाता है:

foreach(fun(H) -> io:format(S, "~p~n",[H]) end, L)

फ़ंक्शन foreach का उपयोग करके, फ़ंक्शन broadcast बन जाता है:

foreach(fun(Pid) -> Pid ! M end, L)

foreach का मूल्यांकन इसके साइड-इफेक्ट के लिए किया जाता है न कि इसके मूल्य के लिए। foreach(Fun ,L) प्रत्येक एलिमेंट X लिए Fun(X) foreach(Fun ,L) कहता है और प्रोसेसिंग L में परिभाषित किए गए क्रम में होता है। map उस क्रम को परिभाषित नहीं करता है जिसमें उसके तत्वों को संसाधित किया जाता है।

२.३ फन का सिंटेक्स

Fun Expressions को निम्नलिखित सिंटैक्स के साथ लिखा जाता है (पूर्ण विवरण के लिए Fun Expressions देखें):

F = fun (Arg1, Arg2, ... ArgN) ->
        ...
    end

यह N तर्कों का एक अनाम कार्य करता है और इसे चर F से बांधता है।

एक अन्य फ़ंक्शन, FunctionName , जो एक ही मॉड्यूल में लिखा गया है, को एक तर्क के रूप में पारित किया जा सकता है, जो निम्नलिखित सिंटैक्स का उपयोग कर रहा है:

F = fun FunctionName/Arity

फ़ंक्शन संदर्भ के इस रूप के साथ, फ़ंक्शन को संदर्भित किया जाता है जिसे मॉड्यूल से निर्यात करने की आवश्यकता नहीं है।

निम्नलिखित सिंटैक्स के साथ एक अलग मॉड्यूल में परिभाषित फ़ंक्शन को संदर्भित करना भी संभव है:

F = fun Module:FunctionName/Arity

इस मामले में, फ़ंक्शन को प्रश्न में मॉड्यूल से निर्यात किया जाना चाहिए।

निम्नलिखित कार्यक्रम फन बनाने के विभिन्न तरीकों को दिखाता है:

-module(fun_test).
-export([t1/0, t2/0]).
-import(lists, [map/2]).

t1() -> map(fun(X) -> 2 * X end, [1,2,3,4,5]).

t2() -> map(fun double/1, [1,2,3,4,5]).

double(X) -> X * 2.

मजेदार F का मूल्यांकन निम्नलिखित वाक्य रचना के साथ किया जा सकता है:

F(Arg1, Arg2, ..., Argn)

यह जाँचने के लिए कि क्या कोई शब्द मज़ेदार है, किसी गार्ड में परीक्षण is_function/1 उपयोग करें।

उदाहरण:

f(F, Args) when is_function(F) ->
   apply(F, Args);
f(N, _) when is_integer(N) ->
   N.

फंड एक विशिष्ट प्रकार है। BIFs erlang:fun_info/1,2 का उपयोग किसी मज़ा के बारे में जानकारी प्राप्त करने के लिए किया जा सकता है, और BIF erlang:fun_to_list/1 एक मजेदार का एक erlang:fun_to_list/1 प्रतिनिधित्व देता है। check_process_code/2 true अगर प्रक्रिया में ऐसे फन हैं जो एक मॉड्यूल के पुराने संस्करण पर निर्भर करते हैं।

एक मजेदार के भीतर 2.4 परिवर्तनीय बाइंडिंग

चरों में होने वाले चरों के लिए गुंजाइश नियम इस प्रकार हैं:

  • एक मजेदार के सिर में होने वाले सभी चर को "ताजा" चर माना जाता है।
  • वेरीएबल्स जो मस्ती से पहले परिभाषित किए गए हैं, और जो फन के भीतर फंक्शन कॉल या गार्ड टेस्ट में होते हैं, वे वे फन हैं जो वे मस्ती के बाहर रखते हैं।
  • चर एक मजेदार से निर्यात नहीं किया जा सकता है।

निम्नलिखित उदाहरण इन नियमों को चित्रित करते हैं:

print_list(File, List) ->
    {ok, Stream} = file:open(File, write),
    foreach(fun(X) -> io:format(Stream,"~p~n",[X]) end, List),
    file:close(Stream).

यहाँ, चर X , मस्ती के सिर में परिभाषित किया गया है, एक नया चर है। चर Stream , जो मज़े के भीतर उपयोग की जाती है, file:open से इसका मूल्य प्राप्त होता file:open रेखा।

जैसा कि किसी भी मौज-मस्ती के सिर में होता है एक नया चर माना जाता है, यह इस प्रकार लिखने के लिए समान रूप से मान्य है:

print_list(File, List) ->
    {ok, Stream} = file:open(File, write),
    foreach(fun(File) -> 
                io:format(Stream,"~p~n",[File]) 
            end, List),
    file:close(Stream).

यहां, X बजाय नए चर के रूप में File का उपयोग किया जाता है। यह इतना बुद्धिमान नहीं है क्योंकि मजेदार बॉडी में कोड वैरिएबल File को संदर्भित नहीं कर सकता है, जो कि मस्ती के बाहर परिभाषित है। इस उदाहरण का संकलन निम्नलिखित निदान देता है:

./FileName.erl:Line: Warning: variable 'File' 
      shadowed in 'fun'

यह इंगित करता है कि चर File , जिसे मस्ती के अंदर परिभाषित किया गया है, चर File टकराती है, जो मस्ती के बाहर परिभाषित होती है।

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

f(...) ->
    Y = ...
    map(fun(X) when X == Y ->
             ;
           (_) ->
             ...
        end, ...)
    ...

निम्नलिखित कोड लिखने के बजाय:

f(...) ->
    Y = ...
    map(fun(Y) ->
             ;
           (_) ->
             ...
        end, ...)
    ...

2.5 फंड और मॉड्यूल सूची

निम्नलिखित उदाहरण एरलांग शेल के साथ एक संवाद दिखाते हैं। सभी उच्च क्रम के कार्यों पर चर्चा मॉड्यूल lists से निर्यात की जाती है।

नक्शा

map एक तर्क का एक समारोह और शर्तों की एक सूची लेता है:

map(F, [H|T]) -> [F(H)|map(F, T)];
map(F, [])    -> [].

यह फ़ंक्शन को सूची में प्रत्येक तर्क पर लागू करके प्राप्त की गई सूची को लौटाता है।

जब शेल में एक नई मस्ती को परिभाषित किया जाता है, तो Fun#<erl_eval> का मूल्य Fun#<erl_eval> रूप में प्रिंट किया जाता है:

> Double = fun(X) -> 2 * X end.
#Fun<erl_eval.6.72228031>
> lists:map(Double, [1,2,3,4,5]).
[2,4,6,8,10]

कोई भी

any एक तर्क और शर्तों की एक सूची का P को पूरा करता है:

any(Pred, [H|T]) ->
    case Pred(H) of
        true  ->  true;
        false ->  any(Pred, T)
    end;
any(Pred, []) ->
    false.

एक विधेय एक फ़ंक्शन है जो true या false लौटाता true any true यदि सूची में X शब्द है जैसे कि P(X) true

एक विधेय Big(X) को परिभाषित किया गया है, जो कि true यदि इसका तर्क अधिक है कि 10:

> Big = fun(X) -> if X > 10 -> true; true -> false end end.
#Fun<erl_eval.6.72228031>
> lists:any(Big, [1,2,3,4]).
false
> lists:any(Big, [1,2,3,12,5]).
true

सब

all पास समान तर्क हैं:

all(Pred, [H|T]) ->
    case Pred(H) of
        true  ->  all(Pred, T);
        false ->  false
    end;
all(Pred, []) ->
    true.

यह true अगर सूची में सभी तत्वों पर लागू विधेय true

> lists:all(Big, [1,2,3,4,12,6]).   
false
> lists:all(Big, [12,13,14,15]).       
true

प्रत्येक के लिए

foreach एक तर्क का एक फ़ंक्शन और शर्तों की एक सूची लेता है:

foreach(F, [H|T]) ->
    F(H),
    foreach(F, T);
foreach(F, []) ->
    ok.

फ़ंक्शन को सूची में प्रत्येक तर्क पर लागू किया जाता है। ok । इसका उपयोग केवल इसके दुष्प्रभाव के लिए किया जाता है:

> lists:foreach(fun(X) -> io:format("~w~n",[X]) end, [1,2,3,4]). 
1
2
3
4
ok

foldl

foldl दो तर्कों का एक कार्य लेता है, एक संचयकर्ता और एक सूची:

foldl(F, Accu, [Hd|Tail]) ->
    foldl(F, F(Hd, Accu), Tail);
foldl(F, Accu, []) -> Accu.

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

यदि आपके पास L = ["I","like","Erlang"] सूचियों की एक सूची है, तो आप L के सभी तारों की लंबाई निम्नानुसार कर सकते हैं:

> L = ["I","like","Erlang"].
["I","like","Erlang"]
10> lists:foldl(fun(X, Sum) -> length(X) + Sum end, 0, L).                    
11

foldl एक अनिवार्य भाषा में while लूप की तरह काम करता है:

L =  ["I","like","Erlang"],
Sum = 0,
while( L != []){
    Sum += length(head(L)),
    L = tail(L)
end

mapfoldl

mapfoldl एक साथ नक्शे और एक सूची से अधिक गुना:

mapfoldl(F, Accu0, [Hd|Tail]) ->
    {R,Accu1} = F(Hd, Accu0),
    {Rs,Accu2} = mapfoldl(F, Accu1, Tail),
    {[R|Rs], Accu2};
mapfoldl(F, Accu, []) -> {[], Accu}.

निम्न उदाहरण दिखाता है कि L में सभी अक्षरों को ऊपरी मामले में कैसे बदलना है और फिर उन्हें गिनना है।

ऊपरी मामले में पहला बदलाव:

> Upcase = fun(X) when $a =< X, X =< $z -> X + $A - $a;
(X) -> X 
end.
#Fun<erl_eval.6.72228031>
> Upcase_word = 
fun(X) -> 
lists:map(Upcase, X) 
end.
#Fun<erl_eval.6.72228031>
> Upcase_word("Erlang").
"ERLANG"
> lists:map(Upcase_word, L).
["I","LIKE","ERLANG"]

अब, गुना और नक्शा एक ही समय में किया जा सकता है:

> lists:mapfoldl(fun(Word, Sum) ->
{Upcase_word(Word), Sum + length(Word)}
end, 0, L).
{["I","LIKE","ERLANG"],11}

फिल्टर

filter एक तर्क और एक सूची का विधेय लेता है और सूची में सभी तत्वों को लौटाता है जो विधेय को संतुष्ट करते हैं:

filter(F, [H|T]) ->
    case F(H) of
        true  -> [H|filter(F, T)];
        false -> filter(F, T)
    end;
filter(F, []) -> [].
> lists:filter(Big, [500,12,2,45,6,7]).
[500,12,45]

नक्शों और छन्दों को मिलाने से बहुत सक्सेस कोड लिखने की सुविधा मिलती है। उदाहरण के लिए, एक सेट अंतर फ़ंक्शन को परिभाषित करने के लिए diff(L1, L2) सूचियों L1 और L2 बीच का अंतर होना चाहिए, कोड निम्नानुसार लिखा जा सकता है:

diff(L1, L2) -> 
    filter(fun(X) -> not member(X, L2) end, L1).

यह L1 में उन सभी तत्वों की सूची देता है जो L2 में सम्‍मिलित नहीं हैं।

सूची L1 और L2 प्रतिच्छेदन को भी आसानी से परिभाषित किया गया है:

intersection(L1,L2) -> filter(fun(X) -> member(X,L1) end, L2).

takewhile

takewhile(P, L) एक सूची L से तत्वों X तक लेती है जब तक कि P(X) सही नहीं है:

takewhile(Pred, [H|T]) ->
    case Pred(H) of
        true  -> [H|takewhile(Pred, T)];
        false -> []
    end;
takewhile(Pred, []) ->
    [].
> lists:takewhile(Big, [200,500,45,5,3,45,6]).  
[200,500,45]

dropwhile

dropwhile का पूरक है:

dropwhile(Pred, [H|T]) ->
    case Pred(H) of
        true  -> dropwhile(Pred, T);
        false -> [H|T]
    end;
dropwhile(Pred, []) ->
    [].
> lists:dropwhile(Big, [200,500,45,5,3,45,6]).
[5,3,45,6]

splitwith

splitwith(P, L) लिस्ट को दो उपविभागों {L1, L2} में विभाजित करता है, जहाँ L = takewhile(P, L) और L2 = dropwhile(P, L) :

splitwith(Pred, L) ->
    splitwith(Pred, L, []).

splitwith(Pred, [H|T], L) ->
    case Pred(H) of 
        true  -> splitwith(Pred, T, [H|L]);
        false -> {reverse(L), [H|T]}
    end;
splitwith(Pred, [], L) ->
    {reverse(L), []}.
> lists:splitwith(Big, [200,500,45,5,3,45,6]).
{[200,500,45],[5,3,45,6]}

2.6 फंड्स रिटर्निंग फन

अब तक, केवल फ़ंक्शंस के रूप में मज़े लेने वाले कार्यों का वर्णन किया गया है। अधिक शक्तिशाली फ़ंक्शंस, जो स्वयं फ़न लौटाते हैं, को भी लिखा जा सकता है। निम्नलिखित उदाहरण इस प्रकार के कार्यों का वर्णन करते हैं।

सरल उच्च आदेश कार्य

Adder(X) एक फ़ंक्शन है जिसे X दिया गया है, एक नया फ़ंक्शन G देता है जैसे G(K) K + X लौटाता है:

> Adder = fun(X) -> fun(Y) -> X + Y end end.
#Fun<erl_eval.6.72228031>
> Add6 = Adder(6).
#Fun<erl_eval.6.72228031>
> Add6(10).
16

अनंत सूचियाँ

विचार कुछ लिखने की है:

-module(lazy).
-export([ints_from/1]).
ints_from(N) ->
    fun() ->
            [N|ints_from(N+1)]
    end.

फिर निम्नानुसार आगे बढ़ें:

> XX = lazy:ints_from(1).
#Fun<lazy.0.29874839>
> XX().
[1|#Fun<lazy.0.29874839>]
> hd(XX()).
1
> Y = tl(XX()).
#Fun<lazy.0.29874839>
> hd(Y()).
2

और इसी तरह। यह "आलसी एम्बेडिंग" का एक उदाहरण है।

पदच्छेद

निम्नलिखित उदाहरण निम्न प्रकार के पार्सर्स दिखाते हैं:

Parser(Toks) -> {ok, Tree, Toks1} | fail

टोकन पार्स किए जाने वाले टोकन की सूची है। एक सफल पार्स रिटर्न {ok, Tree, Toks1}

  • Tree एक पेड़ है।
  • Toks1 Tree की एक पूंछ है जिसमें संरचना के बाद Toks1 प्रतीकों को शामिल किया गया है जिसे सही ढंग से पार्स किया गया था।

एक असफल पार्स रिटर्न fail

निम्नलिखित उदाहरण एक सरल, कार्यात्मक पार्सर दिखाता है जो व्याकरण को पार्स करता है:

(a | b) & (c | d)

निम्नलिखित कोड मॉड्यूल funparse में एक फ़ंक्शन pconst(X) को परिभाषित करता है, जो एक मजेदार रिटर्न देता है जो टोकन की सूची को पार्स करता है:

pconst(X) ->
    fun (T) ->
       case T of
           [X|T1] -> {ok, {const, X}, T1};
           _      -> fail
       end
    end.

इस फ़ंक्शन का उपयोग निम्नानुसार किया जा सकता है:

> P1 = funparse:pconst(a).
#Fun<funparse.0.22674075>
> P1([a,b,c]).
{ok,{const,a},[b,c]}
> P1([x,y,z]).     
fail

अगला, दो उच्च क्रम वाले कार्य pand और por परिभाषित हैं। वे अधिक जटिल पार्सर पैदा करने के लिए आदिम पार्सर को मिलाते हैं।

पहला pand :

pand(P1, P2) ->
    fun (T) ->
        case P1(T) of
            {ok, R1, T1} ->
                case P2(T1) of
                    {ok, R2, T2} ->
                        {ok, {'and', R1, R2}};
                    fail ->
                        fail
                end;
            fail ->
                fail
        end
    end.

व्याकरण G1 लिए एक पार्सर P1 दिया, और व्याकरण G2 लिए एक parser P2 , pand(P1, P2) व्याकरण के लिए एक पार्सर देता है, जिसमें G1 संतुष्ट करने वाले टोकन के अनुक्रम होते हैं, इसके बाद G2 संतुष्ट करने वाले टोकन के अनुक्रम होते हैं।

por(P1, P2) व्याकरण G1 या G2 द्वारा वर्णित भाषा के लिए एक पार्सर देता है:

por(P1, P2) ->
    fun (T) ->
        case P1(T) of
            {ok, R, T1} -> 
                {ok, {'or',1,R}, T1};
            fail -> 
                case P2(T) of
                    {ok, R1, T1} ->
                        {ok, {'or',2,R1}, T1};
                    fail ->
                        fail
                end
        end
    end.

मूल समस्या व्याकरण (a | b) & (c | d) को पार्स करने की थी। निम्नलिखित कोड इस समस्या को हल करता है:

grammar() ->
    pand(
         por(pconst(a), pconst(b)),
         por(pconst(c), pconst(d))).

निम्नलिखित कोड व्याकरण में एक पार्सर इंटरफ़ेस जोड़ता है:

parse(List) ->
    (grammar())(List).

पार्सर का परीक्षण निम्नानुसार किया जा सकता है:

> funparse:parse([a,c]).
{ok,{'and',{'or',1,{const,a}},{'or',1,{const,c}}}}
> funparse:parse([a,d]). 
{ok,{'and',{'or',1,{const,a}},{'or',2,{const,d}}}}
> funparse:parse([b,c]).   
{ok,{'and',{'or',2,{const,b}},{'or',1,{const,c}}}}
> funparse:parse([b,d]). 
{ok,{'and',{'or',2,{const,b}},{'or',2,{const,d}}}}
> funparse:parse([a,b]).   
fail