먼저 Objective Caml은 구문상의 차이에도 불구하고 변경 가능한 데이터 구조 (참조, 배열, 해시 테이블) 및 필수 구성 (for 및 while 루프)을 통해 C++과 상당히 유사한 프로그래밍 스타일을 지원한다는 점에 유의하십시오. , 변수 할당). 나는 당신이 실제로 관용적 인 순수한 기능적 스타일로 위상 적 분류를 쓰려고한다는 것을 아래에서 추측하고 있습니다.
순수 함수 프로그래밍은 대부분 선언적입니다.이 함수는 해당 값 에 적용되며이 다른 값은입니다. 따라서 let x =
의 오른쪽은 return
을 사용하는 일련의 동작 대신 표현식 (값으로 평가)입니다. 물론 일련의 단계로 일반적으로 설명되는 알고리즘을 적용 할 때 문제가 발생합니다.
다행스럽게도 "X 값 변경"을 "이전 값과 동일한 새 오브젝트 반환"으로 전환하여 기능 스타일에서 명령형 알고리즘을 나타낼 수있는 패턴 (실제로는 패밀리 패턴 패밀리)이 있습니다. , X의 값은 제외. "
전통적인 DFS 알고리즘은 어떤 요소가 이미 방문했는지 (일반적으로 (한 번 이상 방문하지 않도록) 및 현재 위치로 이동하는 것을 기억하면서 그래프를 단계별로 진행 함) 사이클). 따라서 DFS 알고리즘의 명령형 상태는 현재 노드, 방문한 노드 집합 및 현재 경로에있는 노드 집합으로 구성됩니다. 이 모든 데이터는 재귀 호출에 제공되어야하며 영구적 인 변경 사항은 동일한 재귀 호출에 의해 반환되어야합니다. 두 세트리스트 표현 (가 거의 최적의 - Set
여기에 더 나은 선택이 될 것입니다)와 함께 위의 그래프 구조를 사용
, 알고리즘은 다음과 같이 다소 보일 것이다
let dfs graph start_node =
let rec explore path visited node =
if List.mem node path then raise (CycleFound path) else
if List.mem node visited then visited else
let new_path = node :: path in
let edges = List.assoc node graph in
let visited = List.fold_left (explore new_path) visited edges in
node :: visited
in explore [] [] start_node
세 위의 중요한 세부 사항 : 첫째, DFS에 따르면 주어진 노드의 모든 하위 노드를 탐색 한 후 "현재 경로"목록에서 해당 노드를 제거하고이를 "방문한"목록에 넣어야합니다. 우리가 불변의 데이터 구조를 사용하기 때문에 첫 번째 단계는 필요하지 않습니다. 탐색의 시작시 노드의 삽입을 취소하는 것이 유일한 목적이며 에 대한 재귀 호출에 의해 목록 new_path
이 수정되지 않습니다. 이것은 기능적 데이터 구조가 명령형 구조보다 작업하기에 더 편한 경우의 예입니다.
또 다른 중요한 세부 사항은 List.fold_left
입니다. 상태를 명시 적으로 만들기 시작할 때 유형 -> unit
의 암시 적 명령형 함수를 -> state -> .. -> state
유형의 명시 적 함수로 대체했습니다 (매개 변수로 상태를 허용하고 새 상태를 반환).그래서,이처럼 보였다 명령형 목록 탐험 :
f edge_1 ; f edge_2 ; f edge_3
이제 다음과 같습니다
let state = f (f (f state edge_1) edge_2) edge_3)
을 List.fold_left f state [edge_1 ; edge_2 ; edge_3]
이 바로 이러한 작업을 수행하는 것입니다 어느. 나만의 경적을 울리고 있지만, 나는 a nice article about this here이있다.
목록을 사용하여 집합을 나타낼 때 "추가 할 요소 추가"작업은 element :: set
으로 작성됩니다. 이는 모든 요소가 포함 된 새 집합 (목록)을 반환하는 연산이기 때문입니다. 원래 요소는 새 요소와 함께 설정됩니다. 이렇게하면 일정한 양의 메모리를 사용하면서 원본 세트가 그대로 유지됩니다 (1 단계에서 설명한 이유 때문에 유용함). 이는 셀에 대한 참조를 포함하는 간단한 머리 - 꼬리 쌍이며 집합에 대한 참조를 포함합니다) : 실행 취소 기능을 제공 할뿐만 아니라 추가 비용없이 그렇게 할 수 있습니다.
위의 알고리즘은 DFS 탐색의 잎으로 시작하여 visited
에 노드를 "삽입"합니다.이 경우 노드는 마지막으로 수행해야하는 노드를 나타냅니다. 즉, 반환 된 목록은 토폴로지별로 정렬되지만 시작점이 유일한 루트 요소가 아니거나 루트 요소가 아닐 수도 있기 때문에 모든 요소가 포함되어 있지 않을 수도 있습니다. 따라서 그래프에서 다른 노드를 가져 와서 모든 그래프를 탐색 할 때까지 추가 처리 단계가 있습니다.
또는, 즉, 그래프의 모든 노드에서 새 DFS 탐사
을 시작하지만, 이전에 모든 노드를 무시가을 탐구 - 다음에 하나 개의 DFS 탐사에서 방문 요소의 목록을 유지에 해당하는 .
let dfs graph visited start_node =
let rec explore path visited node =
if List.mem node path then raise (CycleFound path) else
if List.mem node visited then visited else
let new_path = node :: path in
let edges = List.assoc node graph in
let visited = List.fold_left (explore new_path) visited edges in
node :: visited
in explore [] visited start_node
let toposort graph =
List.fold_left (fun visited (node,_) -> dfs graph visited node) [] graph
팅겨 이미 방문한 노드의 목록을 지정 dfs
의 호출을 허용으로 구성되어 있습니다 : 이전 알고리즘에 작은 비틀기를 사용
,이 두 라인을합니다. 모든 노드에서 DFS를 시작하는 동안 방문한 노드 목록을 전달하는 것은 전에와 마찬가지로 List.fold_left
을 사용하여 수행됩니다.
EDIT : 예외 유형을 제외하고 그래프의 요소 유형을 제한하는 요소는 여기에 없습니다. 그러나 예외는 다형성 일 수 없으므로 두 가지 가능한 솔루션이 있습니다. 첫 번째는 실제로 예외와 함께 모든 데이터를 반환 포기하는 것입니다
exception CycleFound
... raise CycleFound ...
이
다시 ('a * ('a list)) list -> 'a list
더 일반적인에 toposort
의 유형을 되돌아갑니다. 다른 솔루션은 오히려 고급 OCaml이 있습니다 :
module type NODE = sig
type t
end
module type Topo = functor (Node:NODE) -> struct
exception CycleFound of Node.t list
let dfs ...
let sort ...
end
이 유형을 만들 것입니다 : 다음과 같이 예외 및 특정 유형의 다형성 위상 종류를 포함 모듈을해야
귀하의 질문은 다른 말로 표현 되었다면 type recipe = Eggs | Milk | Wheat | Mix | Cook | Serve
module Node = struct
type t = recipe
end
let graph = [ Wheat, [Eggs,Milk,Mix] ;
Milk, [Mix] ;
Eggs, [Mix] ;
Mix, [Cook] ;
Cook, [Serve] ;
Serve, [] ]
module RecipeTopo = Topo(Node)
let sorted = RecipeTopo.sort graph
, 내가 추천했을 Y : 당신은 당신이 그 유형 노드 모듈을 정의하여 원하는 유형을 정렬 할 수 있습니다 의미
(Node.t * (Node.t list)) list -> Node.t list
될Topo(Node).sort
단지 http://ocamlgraph.lri.fr/을 사용하십시오.그럼에도 불구하고 OCaml과 특히 OCaml의 모듈 시스템에 익숙해지면이 예제를 다시 방문하는 것이 좋습니다. Ocamlgraph에 대한 훌륭한 소개는 http://alexleighton.wordpress.com/2009/04/22/intro-to-ocamlgraph/에 있습니다. –