2016-01-07 11 views
6

mi trovo in una situazione in cui posso solo creare funzioni anonime che sono assegnati a una variabile, in questo modo:Le funzioni anonimi multi-area sono possibili in Erlang?

Foo = fun(X) -> X end. 

so che se ci si trova in un modulo che si può fare qualcosa di simile:

foo(X) -> X. 
foo(X, Y) -> X + Y. 
% i.e. foo(3) = 3, foo(3,4) = 7 

Le mie domande sono: questo è possibile con funzioni anonime?

un post sul blog (che ora ho perso mi dispiace) mi ha portato a pensare che si potrebbe fare qualcosa di simile:

Foo = fun(X) -> X; 
     (X, Y) -> X + Y 
     end. 

Ma questo non funziona, come ottengo un errore "testa non corrispondente" .

+0

È possibile farlo solo per la corrispondenza di modelli, ad esempio 'Foo = fun ({x, y}) -> uno; ({a, b}) -> two end', non riesco a pensare a un modo in cui puoi fare ciò che vuoi a meno che tu non accetti semplicemente una lista, come 'Foo = fun ([X]) -> X; ([X, Y]) -> {X, Y} fine', chiamando 'Foo ([a])' ​​e 'Foo ([a, b])', ecc. – Michael

+0

Sì, a parte accettare una lista non riesco a trovare nulla. Nella situazione del mondo reale, ciascuna delle variabili passate sono cose diverse, quindi un'opzione sarebbe quella di passare una lista (io penso che sia una mappa idiomatica, io sono nuovo di Erlang) e farlo in quel modo. Fai del tuo commento una risposta se vuoi un dolce dolce karma: P – SCdF

+1

In realtà è una domanda molto interessante, non ho mai notato o pensato a questo ... – Michael

risposta

2

Si può solo pattern match:

Foo = fun ({x,y}) -> one; 
      ({a,b}) -> two 
      end 

Si può solo accettare una lista:

Foo = fun ([X]) -> X; 
      ([X,Y]) -> {X,Y} 
      end 

ero troppo curioso di non guardare le prestazioni, così ho avuto un rapido andare a benchmark esso. Questo è tutt'altro che perfetto, e inteso solo per essere indicativo di ciò che potrebbe essere, e in realtà si dovrebbe calcolare una media, deviazione standard, media, minima e massima, ma ho optato per un tempo minimo per fare 1.000.000 di chiamate.

Ho usato questo codice:

-module(foo). 

-export([run/1, norm1/0, norm2/0, norm3/0, norm4/0, list1/0, list2/0, list3/0, list4/0]). 

-define(COUNT, 1000000). 


run(F) -> 
    T = lists:foldl(
     fun(_,Min) -> 
      T = ?MODULE:F(), 
      if T < Min -> T; true -> Min end 
      end, 
     ?MODULE:F(), 
     lists:seq(1,99) 
     ), 
    T 
    . 

norm1() -> 
    Foo = fun(N) -> N * N end, 
    {T,_} = timer:tc(fun() -> 
     lists:map(fun(A) -> Foo(A) end, lists:seq(1,?COUNT)) 
     end), 
    T 
    . 

norm2() -> 
    Foo = fun(N, M) -> N * M end, 
    {T,_} = timer:tc(fun() -> 
     lists:map(fun(A) -> Foo(A,A) end, lists:seq(1,?COUNT)) 
     end), 
    T 
    . 

norm3() -> 
    Foo = fun(M, N, M) -> N * M end, 
    {T,_} = timer:tc(fun() -> 
     lists:map(fun(A) -> Foo(A,A,A) end, lists:seq(1,?COUNT)) 
     end), 
    T 
    . 

norm4() -> 
    Foo = fun(N, M, N, M) -> N * M end, 
    {T,_} = timer:tc(fun() -> 
     lists:map(fun(A) -> Foo(A,A,A,A) end, lists:seq(1,?COUNT)) 
     end), 
    T 
    . 

list1() -> 
    Foo = fun([N]) -> N * N end, 
    {T,_} = timer:tc(fun() -> 
     lists:map(fun(A) -> Foo([A]) end, lists:seq(1,?COUNT)) 
     end), 
    T 
    . 

list2() -> 
    Foo = fun([N, M]) -> N * M end, 
    {T,_} = timer:tc(fun() -> 
     lists:map(fun(A) -> Foo([A,A]) end, lists:seq(1,?COUNT)) 
     end), 
    T 
    . 

list3() -> 
    Foo = fun([_, N, M]) -> N * M end, 
    {T,_} = timer:tc(fun() -> 
     lists:map(fun(A) -> Foo([A,A,A]) end, lists:seq(1,?COUNT)) 
     end), 
    T 
    . 

list4() -> 
    Foo = fun([_, _, N, M]) -> N * M end, 
    {T,_} = timer:tc(fun() -> 
     lists:map(fun(A) -> Foo([A,A,A,A]) end, lists:seq(1,?COUNT)) 
     end), 
    T 
    . 

Questo è il risultato:

1> foo:run(norm1). 
44820 
2> foo:run(norm2). 
48959 
3> foo:run(norm3). 
50328 
4> foo:run(norm4). 
50402 
5> 
5> foo:run(list1). 
50463 
6> foo:run(list2). 
58948 
7> foo:run(list3). 
60829 
8> foo:run(list4). 
86604 
9> 

Le prestazioni sono ovviamente andando a dipendere da quanto tempo la lista è, come ci si aspetterebbe, in quanto deve lavorare attraverso la lista e la penalità è maggiore rispetto a una chiamata normale.

La differenza di prestazioni non mi sembra così grande per quanto valga la pena di preoccuparsi nella maggior parte delle situazioni se è la soluzione corretta, almeno, se non ci si aspetta di avere troppi argomenti nelle proprie liste!

4

La risposta semplice e corretta è no, non è possibile. Questo è in realtà abbastanza logico come quando si fanno

foo(X) -> X. 
foo(X, Y) -> X + Y. 

in realtà si sta creando due funzioni: foo/1, una funzione di un argomento; e foo/2, un'altra funzione che ha due argomenti. Sono non lo stesso foo. Questo mappa direttamente alle funzioni anonime (funs) quindi è necessario creare due diversi divertimenti, uno di un argomento e l'altro di due argomenti.

L'errore "mancata corrispondenza della testina" si lamenta del diverso numero di argomenti.