2012-09-29 8 views
5

Come un semplice esempio, supponiamo di avere un elenco di numeri L e voglio trovare il primo elemento che è maggiore di un numero specifico X. Potrei farlo con list comprehension come questo:Erlang: Primo elemento in una lista che corrisponde ad alcune condizioni (senza valutare il resto degli elementi)

([email protected])24> L = [1, 2, 3, 4, 5, 6].    
[1,2,3,4,5,6] 
([email protected])25> X = 2.5. 
2.5 
([email protected])26> [First | _] = [E || E <- L, E > X]. 
[3,4,5,6] 
([email protected])27> First. 
3 

ma questo sembra potenzialmente molto inefficiente, dal momento che l'elenco potrebbe essere molto lungo e la prima partita potrebbe essere nella fase iniziale. Quindi mi chiedo se a) Esiste un modo efficace per fare ciò che non valuterà il resto degli elementi nella lista dopo che è stata trovata la prima corrispondenza? oppure b) Quando viene compilato, Erlang ottimizza comunque il resto dei confronti?

Questo è come vorrei ottenere ciò che sto cercando in C:

int first_match(int* list, int length_of_list, float x){ 
    unsigned int i; 
    for(i = 0; i < length_of_list, i++){ 
     if(x > list[i]){ return list[i]; } /* immediate return */ 
    } 
    return 0.0; /* default value */ 
} 

risposta

11

bene, qualcosa di simile a

firstmatch(YourList, Number) -> 
    case lists:dropwhile(fun(X) -> X =< Number end, YourList) of 
    [] -> no_solution; 
    [X | _] -> X 
    end. 
+1

Bello. Questo è più conciso della mia soluzione. Ho dovuto fare un piccolo tentativo per verificare che 'dropwhile' non smettesse di valutare dopo la prima partita fallita. L'ho avvolto in una funzione che ti permette di specificare la condizione come una funzione (senza dover invertire la logica): https://gist.github.com/3807110 – dantswain

3

Ecco cosa ho stato in grado di elaborare. Mi piacerebbe ancora sapere se c'è una risposta migliore e/o se la cosa più semplice viene ottimizzata (più ci penso, più ne dubito).

-module(lazy_first). 

-export([first/3]). 

first(L, Condition, Default) -> 
    first(L, [], Condition, Default). 

first([E | Rest], Acc, Condition, Default) -> 
    case Condition(E) of 
    true -> E; 
    false -> first(Rest, [E | Acc], Condition, Default) 
    end; 

first([], _Acc, _Cond, Default) -> Default. 

Esempio:

14> lazy_first:first([1, 2, 3, 4, 5], fun(E) -> E > 2.5 end, 0.0). 
3 
15> lazy_first:first([1, 2, 3, 4, 5], fun(E) -> E > 5.5 end, 0.0). 
0.0 

Modifica

Ecco una versione senza un accumulatore.

first([E | Rest], Condition, Default) -> 
    case Condition(E) of 
    true -> E; 
    false -> first(Rest, Condition, Default) 
    end; 

first([], _Cond, Default) -> Default. 
+2

non può affrontare la questione se ci sia un collegamento integrato, ma questo appare certamente come il modo corretto per lanciare il tuo ... eccetto il tuo accumulatore sembra non necessario. – macintux

+0

Davvero un buon punto :) Questo in realtà lo semplifica un po '. – dantswain

3

Ecco una soluzione rapida:

first_greater([],_) -> undefined; 
first_greater([H|_], Num) when H > Num -> H; 
first_greater([_|T], Num) -> first_greater(T,Num).