Как реализовать графы и графовые алгоритмы на функциональном языке программирования? - PullRequest
43 голосов
/ 08 июня 2010

По сути, я знаю, как создавать структуры данных графа и использовать алгоритм Дейкстры в языках программирования, где разрешены побочные эффекты. Как правило, алгоритмы графа используют структуру, чтобы пометить определенные узлы как «посещенные», но это имеет побочные эффекты, которых я стараюсь избегать.

Я могу придумать один способ реализовать это на функциональном языке, но в основном это требует передачи большого количества состояния различным функциям, и мне интересно, есть ли более экономичное решение.

Ответы [ 6 ]

17 голосов
/ 08 июня 2010

Вы можете проверить, как работает библиотека функциональных графов Haskell Мартина Эрвига . Например, его функции кратчайшего пути все чистые, и вы можете увидеть исходный код о том, как это реализовано.

Другой вариант, , такой как fmark, упомянутый , заключается в использовании абстракции, которая позволяет реализовывать чистые функции в терминах состояния. Он упоминает Государственную монаду (которая доступна как в ленивых , так и строгих разновидностях). Другой вариант, если вы работаете в компиляторе / интерпретаторе GHC Haskell (или, как мне кажется, в любой реализации Haskell, которая поддерживает типы ранга 2), другой вариант - монада ST , которая позволяет писать чистые функции, которые имеют дело с изменяемыми переменными внутри.

3 голосов
/ 08 июня 2010

Если вы используете haskell, единственный функциональный язык, с которым я знаком, я бы рекомендовал использовать State monad . Монада State является абстракцией для функции, которая принимает состояние и возвращает промежуточное значение и некоторое новое значение состояния. Это считается идиоматический haskell для тех ситуаций, когда необходимо поддерживать большое состояние.

Это гораздо более приятная альтернатива наивному "возвращаемому состоянию как результат функции и передать его в качестве параметра", что подчеркивается в учебниках по функциональному программированию для начинающих. Я полагаю, что большинство функциональных языков программирования имеют аналогичную конструкцию.

2 голосов
/ 09 июня 2010

Я просто сохраняю посещенный набор как набор и передаю его в качестве параметра. Существуют эффективные реализации во время журнала наборов любого упорядоченного типа и сверхэффективные наборы целых чисел.

Для представления графа я использую списки смежности, или я буду использовать конечную карту, которая отображает каждый узел в список его наследников. Это зависит от того, что я хочу сделать.

Вместо Абелсона и Суссмана я рекомендую чисто функциональных структур данных Криса Окасаки . Я связался с диссертацией Криса, но если у вас есть деньги, он расширил их до превосходной книги .


Только ради ухмылки, вот немного пугающий обратный поиск в глубину в обратном порядке, выполненный в стиле продолжения в Haskell. Это прямо из библиотеки оптимизатора Hoopl :

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
0 голосов
/ 29 июля 2017

Вот пример Swift. Вы можете найти это немного более читабельным. Переменные на самом деле имеют описательные имена, в отличие от суперскриптических примеров на Haskell.

https://github.com/gistya/Functional-Swift-Graph

0 голосов
/ 08 июня 2010

Я хотел бы услышать о какой-нибудь действительно умной технике, но я думаю, что есть два фундаментальных подхода:

  1. Изменить некоторый объект глобального состояния. то есть побочные эффекты
  2. Передайте график в качестве аргумента вашим функциям с возвращаемым значением, являющимся измененным графиком. Я полагаю, что это ваш подход к "обходу большого количества государства"

Вот что сделано в функциональном программировании. Если компилятор / интерпретатор хорош, он поможет вам управлять памятью. В частности, вам нужно убедиться, что вы используете хвостовую рекурсию, если вам случается выполнять рекурсию в любой из ваших функций.

0 голосов
/ 08 июня 2010

Большинство функциональных языков поддерживают внутренние функции.Таким образом, вы можете просто создать свое графическое представление во внешнем слое и просто ссылаться на него из внутренней функции.

В этой книге подробно рассматриваются 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

...