2010-06-08 13 views
38

Zasadniczo wiem, jak tworzyć struktury danych wykresów i używać algorytmu Dijkstry w językach programowania, w których dozwolone są efekty uboczne. Zazwyczaj algorytmy wykresów używają struktury do oznaczania pewnych węzłów jako "odwiedzanych", ale ma to skutki uboczne, których próbuję uniknąć.Jak zaimplementować wykresy i algorytmy wykresów w funkcjonalnym języku programowania?

Potrafię wymyślić jeden sposób wdrożenia tego w języku funkcjonalnym, ale zasadniczo wymaga on przekazywania dużej ilości stanów różnym funkcjom i zastanawiam się, czy istnieje bardziej efektywne rozwiązanie kosmiczne.

Odpowiedz

15

Możecie sprawdzić jak Martin Erwig za Haskell functional graph library robi rzeczy. Na przykład jej shortest-path functions są czyste, a można zobaczyć, jak jest zaimplementowany source code.

Inną opcją, like fmark mentioned, jest użycie abstrakcji, która pozwala na wdrożenie czystych funkcji pod względem stanu. Wymienia monadę państwową (która jest dostępna w odmianach lazy i strict). Inną opcją, jeśli pracujesz w kompilatorze/interprerze GHC Haskella (lub, jak sądzę, w dowolnej implementacji Haskella obsługującej typy rang 2), jest inna opcja: the ST monad, która umożliwia pisanie czystych funkcji, które zajmują się zmiennymi zmiennymi wewnętrznie .

+7

Wyciągnąłem te linki od strony funkcjonalnej biblioteki wykresu: Wyjaśnia teorię: http://web.engr.oregonstate.edu/~erwig/papers/abstracts.html#JFP01 Ta dokumentuje szczegóły programowania: http://web.engr.oregonstate.edu/~erwig/fgl/haskell/old/fgl0103.pdf Razem, te artykuły najlepiej odpowiadają na moje pytanie, szczególnie drugie, które jest nieco bardziej praktyczne. Dzięki! – brad

-1

Większość języków funkcjonalnych obsługuje funkcje wewnętrzne. Możesz więc po prostu utworzyć reprezentację wykresu w najbardziej zewnętrznej warstwie i po prostu odwołać się do niej z wewnętrznej funkcji.

Książka ta obejmuje go obszernie http://www.amazon.com/gp/product/0262510871/ref=pd_lpo_k2_dp_sr_1?ie=UTF8&cloe_id=aa7c71b1-f0f7-4fca-8003-525e801b8d46&attrMsgId=LPWidget-A1&pf_rd_p=486539851&pf_rd_s=lpo-top-stripe-1&pf_rd_t=201&pf_rd_i=0262011530&pf_rd_m=ATVPDKIKX0DER&pf_rd_r=114DJE8K5BG75B86E1QS

0

chciałbym usłyszeć o jakimś naprawdę sprytne techniki, ale myślę, że istnieją dwa podstawowe podejścia:

  1. zmodyfikować niektóre globalnego obiektu państwowego. tj. efekty uboczne
  2. Przekaż wykres jako argument do swoich funkcji, zwracając wartość będącą zmodyfikowanym wykresem. Zakładam, że to jest twoje podejście polegające na "przepuszczaniu dużych ilości stanów".

Tak właśnie działa się w programowaniu funkcjonalnym. Jeśli kompilator/interpreter jest dobry, pomoże Ci zarządzać pamięcią. W szczególności będziesz chciał się upewnić, że używasz rekurencji ogona, jeśli zdarzy ci się powtórzyć w którejkolwiek z twoich funkcji.

3

Jeśli używasz haskell, jedynego języka funkcjonalnego, z którym jestem zaznajomiony, polecam używanie State monad. Monada stanu jest abstrakcją dla funkcji, która przyjmuje stan i zwraca wartość pośrednią i pewną nową wartość stanu. Jest to considered idiomatic haskell dla tych sytuacji, w których konieczne jest utrzymanie dużego stanu.

Jest to znacznie przyjemniejsza alternatywa dla naiwnego "zwrotu stanu jako wyniku funkcji i przekazania go jako parametr" idiomu, który jest podkreślany w początkowych przewodnikach programowania funkcjonalnego. Wyobrażam sobie, że większość funkcjonalnych języków programowania ma podobną konstrukcję.

+0

byłoby sprawiedliwe, by opisać stan monady jako budowanie pojedynczy kawałek po kawałku funkcyjny, a następnie zastosowanie wypełniony funkcji państwa? – Greg

+0

@Greg: tak. Monadizing niektórych idiomów programowania (w tym przypadku odczyt/zapis na stanie globalnym) jest sposobem na przekształcenie tego idiomu w części składowe. Łączymy kawałki, aby uzyskać większe kawałki. – dubiousjim

2

ja po prostu zachować odwiedzanym ustawiony jako zestaw i przekazać ją jako parametr. Istnieją wydajne implementacje log-time zestawów dowolnego uporządkowanego typu i bardzo wydajnych zestawów liczb całkowitych.

do reprezentowania wykresu używam list sąsiedztwa lub użyję skończoną mapę, która odwzorowuje każdy węzeł do listy swoich następców. To zależy od tego, co chcę zrobić.

Zamiast Abelsona i Sussmana polecam Chrisa Okasakiego Purely Functional Data Structures. Mam powiązania z rozprawą Chrisa, ale jeśli masz pieniądze, rozwinął je w excellent book.


Po prostu dla uśmiechu, oto nieco przerażające, odwrócone postorderowe wyszukiwanie w pierwszej kolejności w stylu kontynuacji-przemijania w Haskell. Jest to prosto z biblioteki Hoopl optymalizatora:

postorder_dfs_from_except :: forall block e . (NonLocal block, LabelsPtr e) 
          => LabelMap (block C C) -> e -> LabelSet -> [block C C] 
postorder_dfs_from_except blocks b visited = 
vchildren (get_children b) (\acc _visited -> acc) [] visited 
where 
    vnode :: block C C -> ([block C C] -> LabelSet -> a) 
         -> ([block C C] -> LabelSet -> a) 
    vnode block cont acc visited = 
     if setMember id visited then 
      cont acc visited 
     else 
      let cont' acc visited = cont (block:acc) visited in 
      vchildren (get_children block) cont' acc (setInsert id  visited) 
     where id = entryLabel block 
    vchildren bs cont acc visited = next bs acc visited 
     where next children acc visited = 
       case children of []  -> cont acc visited 
           (b:bs) -> vnode b (next bs) acc  visited 
    get_children block = foldr add_id [] $ targetLabels bloc 
    add_id id rst = case lookupFact id blocks of 
         Just b -> b : rst 
         Nothing -> rst 
Powiązane problemy