2010-07-19 5 views
5

Mam problem z kodowaniem w Erlang, który jest prawdopodobnie typowym wzorcem projektowym, ale nie mogę znaleźć żadnych informacji, jak go rozwiązać.Wzór projektu? Funkcja iterująca po liście w poszukiwaniu pierwszego wyniku {success}

Mam listę L. Chcę zastosować funkcję f do każdego elementu w L, i mieć go uruchomić we wszystkich elementach w L jednocześnie. Każde wywołanie f (Element) zakończy się albo zakończy się niepowodzeniem; w większości przypadków to się nie powiedzie, ale od czasu do czasu uda mu się to dla konkretnego elementu w obrębie L.

Jeśli/kiedy af (Element) się powiedzie, chcę powrócić "sukces" i zakończyć wszystkie wywołania f dla inne elementy w L - pierwszy "sukces" jest wszystkim, czym jestem zainteresowany. Z drugiej strony, jeśli f (Element) zawodzi dla każdego elementu w L, wtedy chcę zwrócić "fail".

Jako trywialny przykład załóżmy, że L jest listą liczb całkowitych, a F zwraca {sukces}, jeśli element w L wynosi 3, lub {fail} w przypadku dowolnej innej wartości. Chcę znaleźć tak szybko jak to możliwe, jeśli są jakieś 3s w L; Nie dbam o to, ile jest 3, tylko o to, czy co najmniej jeden istnieje, czy nie. f mogłaby wyglądać następująco:

f(Int) -> 
    case Int of 
    3 -> {success}; 
    _ -> {fail} 
    end. 

Jak mogę iterację listy Ints aby dowiedzieć się, czy lista zawiera co najmniej jedną 3 i wrócić tak szybko, jak to możliwe?

Z pewnością jest to wspólny funkcjonalne wzornictwo, a ja po prostu nie używając prawidłowych warunków wyszukiwania w Google ...

Odpowiedz

2

Jak już zostało udzielone rozwiązanie, należy użyć list: any/2.

Widząc, że chcesz współbieżne wersję nim:

any(F, List) -> 
    Parent = self(), 
    Pid = spawn(fun() -> spawner(Parent, F, List) end), 
    receive {Pid, Result} -> Result 
    end, 
    Result. 

spawner(Parent, F, List) -> 
    Spawner = self(), 
    S = spawn_link(fun() -> wait_for_result(Spawner, Parent, length(List)) end), 
    [spawn_link(fun() -> run(S, F) end) || X <- List], 
    receive after infinity -> ok end. 

wait_for_result(Spawner, Parent, 0) -> 
    Parent ! {Spawner, false}, 
    exit(have_result); 
wait_for_result(Spawner, Parent, Children) -> 
    receive 
    true -> Parent ! {Spawner, true}, exit(have_result); 
    false -> wait_for_result(Spawner, Parent, Children -1) 
    end. 

run(S, F) -> 
    case catch(F()) of 
    true -> S ! true; 
    _ -> S ! false 
    end. 

pamiętać, że wszystkie dzieci (procesy „Run”) umrze, gdy proces „wait_for_children” ma wyjście (have_result).

Całkowicie nietestowana ... Ah, co do cholery. Zrobię przykład:

4> play:any(fun(A) -> A == a end, [b,b,b,b,b,b,b,b]). 
false 
5> play:any(fun(A) -> A == a end, [b,b,b,b,b,b,a,b]). 
true 

Nadal mogą występować błędy (i prawdopodobnie istnieją).

+0

Możesz to zoptymalizować, zdając sobie sprawę, że w 'wait_for_result/2' nie jesteśmy naprawdę zainteresowani tym, który pracownik zwraca' false', tylko ilu to zrobiło. Wystarczy więc usunąć pierwszy element listy za każdym razem. – rvirding

+0

Powinieneś również wspomnieć, że wykonanie 'exit (have_result)' zabije wszystkie pozostałe procesy robocze, ponieważ są one połączone (rozpoczynane z 'spawn_link') i' have_result' nie jest 'normalne', więc jest traktowane jako wyjście błędu. – rvirding

+0

Masz oczywiście rację. Zaktualizował odpowiedź swoimi komentarzami. –

4

Istnieją zasadniczo dwa różne sposoby osiągnięcia tego celu. Albo napisać własną funkcję, która iteruje listy powracającego true lub false w zależności od tego, czy uzna 3:

contains_3([3|_]) -> true; 
contains_3([_|T]) -> contains_3(T); 
contains_3([]) -> false. 

Druga jest użyć już określoną funkcję do wykonania właściwej iteracji aż do testu na elementy listy jest prawdą i dostarczyć jej test. lists:any powraca true lub false w zależności od tego, czy test się powiedzie, przez co najmniej jednego elementu:

contains_3(List) -> lists:any(fun (E) -> E =:= 3 end, List). 

zrobi to samo. To, co wybierzesz, zależy od Ciebie. Drugi prawdopodobnie byłby bliższy wzorowi projektowemu, ale czuję, że nawet jeśli go używasz, powinieneś wiedzieć, jak to działa wewnętrznie. W tym przypadku jest to trywialne i bardzo bliskie jawnemu przypadkowi.

To bardzo powszechna rzecz, ale czy można ją zaklasyfikować jako wzór, którego nie znam. Wydaje się to tak podstawowe iw pewnym sensie "banalne", że zawahałbym się nazwać to wzorcem projektowym.

+0

Dzięki rvirding, wygląda na to, że mój przykład był zbyt trywialny ... Mam dość złożoną i czasochłonną funkcję, która musi działać w odniesieniu do każdego elementu na liście. Chcę go uruchomić jednocześnie z każdym elementem na liście, więc mogę uzyskać {sukces} wynik z powrotem JAK NAJSZYBCIEJ, jeśli taki istnieje. Jednakże, gdy już otrzymam pierwszy {sukces}, nie ma sensu, aby funkcja działała dalej w stosunku do każdego innego elementu listy - zajmie to zbyt dużo czasu i przeżuje zbyt wiele zasobów, bez żadnych korzyści. Na koniec, jeśli nie ma {sukcesu} dla żadnego z elementów listy, to muszę to również rozpoznać. – monch1962

+0

Mam nadzieję, że pojawi się rozwiązanie oparte na np. odradza się wiele funkcji (jedna na element listy), mając je uruchomione jednocześnie, a następnie odeślij {sukces} lub {niepowodzenie} procesowi nadrzędnemu. Jest to łatwe do zbudowania przy użyciu listy zrozumiałej i pętli odbiorczej uruchomionej w procesie nadrzędnym; wyzwania są następujące: (a) w pierwszym {success} otrzymanym, powiedz innym podprocesom, aby przerwać przetwarzanie i wyjść ASAP, i (b) obsłużyć przypadek, w którym każdy wynik podprocesu jest {fail}, więc rodzic nigdy nie widzi sukces}. – monch1962

3

Minęło trochę czasu, odkąd zrobiłem erlang, więc nie zamierzam podawać ci składni, jednak erlang i OTP mają rozwiązanie, które czeka na ciebie.

Spawn jeden proces reprezentujący funkcję; niech iteruje nad listą, odkładając tyle procesów, ile uznasz za stosowne, aby skutecznie wykonywać obliczenia na elementach.

Połączyć każdy proces z funkcją-procesem i zakończyć proces funkcji po zwróceniu pierwszego wyniku.

Niech erlang/otp wyczyści resztę procesów.

+0

To ma wiele sensu - spróbuję później jeszcze dziś. Dzięki za sugestię – monch1962

0

Możecie zajrzeć do modułu plists: http://code.google.com/p/plists/ Chociaż nie wiem, czy plists:any obsługuje

(a) w dniu 1} {sukces otrzymanych powiedzieć inne procesy składowe, aby zatrzymać przetwarzanie & exit ASAP

Powiązane problemy