'삼림 벌채'는 프로그램에서 '나무'를 어떻게 제거합니까?


12

삼림 벌채가 어떻게 폴드와 펼치기 기능에서 동시에 목록을 소비하고 생성하는지 이해합니다. ( CodeReview 에서이 좋은 답변을 참조하십시오.)이 기술에 대한 위키 백과 항목 과 비교했을 때 '제거'에 대해 이야기했습니다. 프로그램에서 나무.

프로그램을 구문 분석 구문 분석 트리로 구문 분석하는 방법을 이해하지만 (그렇습니다?) 어떤 종류의 프로그램 단순화를 위해 산림 벌채를 사용한다는 의미는 무엇입니까? 그리고 내 코드에 어떻게해야합니까?

답변:


9

Yatima2975가 첫 두 질문을 다룬 것 같습니다. 세 번째 질문을 다룰 것입니다. 이를 위해 비현실적으로 간단한 사례를 다루 겠지만보다 현실적인 것을 상상할 수있을 것입니다.

차수 의 전체 이진 트리의 깊이를 계산한다고 가정합니다 . (레이블이없는) 이진 트리의 유형은 다음과 같습니다 (하스켈 구문).n

type Tree = Leaf | Node Tree Tree

이제 주문 의 전체 트리는 다음과 같습니다.n

full : Int -> Tree
full n | n == 0 = Leaf
full n = Node (full (n-1)) (full (n-1))

그리고 나무의 깊이는

depth : Tree -> Int
depth Leaf = 0
depth (Node t1 t2) = 1 + max (depth t1) (depth t2)

이제 은 먼저 사용하여 차수의 전체 트리를 생성 한 다음 사용하여 해당 트리를 해체 한다는 것을 알 수 있습니다. 삼림 벌채는 그러한 패턴 (건축 후 해체)이 종종 단락 될 수 있다는 관찰에 의존합니다 : 에 대한 호출을 에 대한 단일 호출로 대체 할 수 있습니다 :n f udepth (full n)nd e p t h d e p t h ( f u l l n ) f u l l _ d e p t hfulldepthdepth (full n)full_depth

full_depth : Int -> Int
full_depth n | n == 0 = 0
full_depth n = 1 + max (full_depth (n-1)) (full_depth (n-1))

이렇게하면 전체 트리의 메모리 할당과 패턴 일치를 수행 할 필요가 없으므로 성능이 크게 향상됩니다. 또한 최적화를 추가하면

max t t --> t

그런 다음 지수 시간 절차를 선형 시간 1로 가 정수의 동일성 이라는 것을 인식하는 추가 최적화가 있으면 멋지 지만 그러한 것은 확실하지 않습니다. 실제로 최적화가 사용됩니다.full_depth

자동 삼림 벌채를 수행하는 유일한 주류 컴파일러는 GHC이며 올바르게 호출하면 내장 기능을 작성할 때 (기술적 인 이유로) 수행됩니다.


본질적으로 동일한 영역을 포함하더라도 다른 답변보다 공식화 된 방식 으로이 답변을 더 많이 얻었으므로 수여됩니다.
Cris Stringfellow

6

첫째, 목록은 일종의 나무입니다. 리스트를 링크 된 리스트로 나타내면 각 노드에 1 또는 0 자손이 있는 트리 일뿐 입니다.

구문 분석 트리는 데이터 구조로 트리를 활용하는 것입니다. 트리에는 정렬, 맵 구현, 연관 배열 등 컴퓨터 과학에 많은 응용 프로그램이 있습니다.

일반적으로 목록, 트리 등은 재귀 데이터 구조입니다. 각 노드에는 일부 정보와 동일한 데이터 구조의 다른 인스턴스가 있습니다. 폴딩은 노드를 "하단"값으로 재귀 적으로 변환하는 모든 이러한 구조에 대한 작업입니다. 펼치기는 반대 과정으로, 값을 "하향식"노드로 변환합니다.

주어진 데이터 구조에 대해 기계적으로 폴딩 및 언 폴딩 기능을 구성 할 수 있습니다.

예를 들어, 목록을 보자. (입력 된 예제와 구문이 매우 깨끗하므로 Haskell을 사용하겠습니다.) List는 끝이거나 값이며 "꼬리"입니다.

data List a = Nil | Cons a (List a)

이제리스트를 접고 있다고 상상해 봅시다. 각 단계에서 현재 노드를 접고 재귀 하위 노드를 이미 접었습니다. 이 상태를 다음과 같이 나타낼 수 있습니다

data ListF a r = NilF | ConsF a r

여기서 r하위 목록을 접어 구성한 중간 값입니다. 이를 통해 목록에 대해 접기 기능을 표현할 수 있습니다.

foldList :: (ListF a r -> r) -> List a -> r
foldList f Nil            = f NilF
foldList f (Cons x xs)    = f (ConsF x (foldList f xs))

우리는 변환 ListListF재귀의 하위 목록을 통해 접어서 다음에 정의 된 함수를 사용합니다 ListF. 당신이 그것에 대해 생각하면, 이것은 표준의 또 다른 표현 일뿐입니다 foldr.

foldr :: (a -> r -> r) -> r -> List a -> r
foldr f z = foldList g
  where
    g NilF          = z
    g (ConsF x r)   = f x r

우리는 unfoldList같은 방식으로 구성 할 수 있습니다 .

unfoldList :: (r -> ListF a r) -> r -> List a
unfoldList f r = case f r of
                  NilF        -> Nil
                  ConsF x r'  -> Cons x (unfoldList f r')

다시 말하지만, 이것은 또 다른 표현입니다 unfoldr.

unfoldr :: (r -> Maybe (a, r)) -> r -> [a]

(의 Maybe (a, r)동형 ListF a r입니다.)

삼림 벌채 기능도 구성 할 수 있습니다.

deforest :: (ListF a r -> r) -> (s -> ListF a s) -> s -> r
deforest f u s = f (map (deforest f u) (u s))
  where
    map h NilF        = NilF
    map h (ConsF x r) = ConsF x (h r)

단순히 중간체를 빼고 List접는 기능과 접는 기능을 융합합니다.

모든 재귀 데이터 구조에 동일한 절차를 적용 할 수 있습니다. 예를 들어, 노드에 0, 1, 2 또는 1 또는 0 분기 노드에 값이있는 자손이있는 트리는 다음과 같습니다.

data Tree a = Bin (Tree a) (Tree a) | Un a (Tree a) | Leaf a

data TreeF a r = BinF r r | UnF a r | LeafF a

treeFold :: (TreeF a r -> r) -> Tree a -> r
treeFold f (Leaf x)       = f (LeafF x)
treeFold f (Un x r)       = f (UnF x (treeFold f r))
treeFold f (Bin r1 r2)    = f (BinF (treeFold f r1) (treeFold f r2))

treeUnfold :: (r -> TreeF a r) -> r -> Tree a
treeUnfold f r = case f r of
                  LeafF x         -> Leaf x
                  UnF x r         -> Un x (treeUnfold f r)
                  BinF r1 r2      -> Bin (treeUnfold f r1) (treeUnfold f r2)

물론 deforestTree이전과 마찬가지로 기계적으로 만들 수 있습니다 .

일반적 treeFold으로 다음과 같이 더 편리하게 표현 합니다.

treeFold' :: (r -> r -> r) -> (a -> r -> r) -> (a -> r) -> Tree a -> r

)

세부 사항을 생략하고 패턴이 분명하기를 바랍니다.

또한보십시오:


좋은 답변, 감사합니다. 링크와 자세한 예는 귀중합니다.
Cris Stringfellow

3

약간 혼란 스럽지만 (컴파일 타임에) 삼림 벌채가 적용되어 (런타임에) 생성되는 중간 트리를 제거합니다. 삼림 벌채는 추상 구문 트리의 일부를 해킹하는 것을 포함하지 않습니다 (죽은 가지 제거 :-)

당신을 버릴 수있는 또 다른 것은 목록 나무이며 매우 균형이 맞지 않는 것입니다!


아. 넵. 매우 불균형!
Cris Stringfellow
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.