실제로 재귀 적으로 계속해야합니다. 현재 귀하의 코드에서 makePaths
은 findPaths
을 호출하지만 findPaths
도 아니고 makePaths
도 makePaths
또는 findPaths
을 반복적으로 호출하지 않습니다. 두 가지 이유 때문에 알고리즘의 메커니즘을 보는 것이 약간 어렵습니다. 첫째, 나무에 많은 임시 상태를 주석으로 추가하고 두 번째로 불필요하게 Set
을 처리하고 있습니다.
그런 것들을 버리자.
트리부터 시작해 보겠습니다. 궁극적으로 노드에 값이있는 n -ary 트리 만 있으면됩니다.
data Tree a = Empty | Node a [Tree a] deriving (Show, Read, Eq, Ord)
명확하게하기 위해,이 Tree
은 동일합니다 당신의 궁극적 인 목표 나무는 우리가 목표로하는거야 String
들과 노드에 장식 된 것 하나이기 때문에, 말했다 Tree
type OldTree a h d = Tree (a, h, d)
makeTree :: String -> [String] -> Tree String
여기서 첫 번째 문자열은 시드 값이고, 문자열 목록은 가능한 연속성입니다. 문자열은 남아 있고, 트리는 우리가 만든 문자열의 나무입니다. 이 함수는 직접 작성할 수도 있습니다.
makeTree seed vals = Node seed children where
children = ...
아이들은 자신의 서브 트리를 구축하여 반복적으로 진행 : 그것은 반복적으로 우리가 바로 우리의 트리의 루트를 알고 씨앗을 제공한다는 사실을 기반으로 진행된다. 이것은 의 문자열을 새로운 시드로 사용하는 것을 제외하고는 지금까지 실행 한 알고리즘의 정확한 복사본입니다. 이렇게하려면 목록을 "선택한 값"목록으로 분할하는 알고리즘을 원합니다.
selectEach :: [a] -> [(a, [a])]
같은 비슷해 그 목록 c:extras
lst
는 다른 순서로, 아마도 것처럼 모두 같은 값을 가진다 (c, extras)
같은 elem (c, extras) (selectEach lst)
하는 각 값에 대해. 결과는 세 가지 등으로 구분된다 어디
selectEach :: [a] -> [([a], a, [a])]
으로, 그러나 조금 다르게이 기능을 쓸거야 그 (before, here, after)
이 값 elem (before, here, after) (selectEach lst)
다음 lst == reverse before ++ [here] ++ after
경우. 이것은 우리가 쉽게 우리 나무의 자녀를 생성 할 수 있습니다이 보조 기능
selectEach [] = []
selectEach (a:as) = go ([], a, as) where
go (before, here, []) = [(before, here, [])]
go (before, here, [email protected](a:as)) = (before, here, after) : go (here:before, a, as)
> selectEach "foo"
[("",'f',"oo"),("f",'o',"o"),("of",'o',"")]
좀 더 쉽게 판명 할 것이다 그러나 우리는 너무 많이 생성 종료됩니다.
makeTree seed vals = Node seed children where
children = map (\(before, here, after) -> makeTree here (before ++ after))
(selectEach vals)
사실 너무 많습니다. 우리가
makeTree "sip" ["sour","piss","rune","profit","today","rat"]
을 실행한다면 우리는 크기 1957 대신 우리가 원하는 크기 8의 좋은 편리한 나무의 나무를 생산하고있어. 이것은 우리가 지금까지 시드의 마지막 글자가 계속하기 위해 선택된 값의 첫 글자 여야한다는 제약 조건을 없앴기 때문입니다. 우리는 나쁜 나무를 걸러 냄으로써 그 문제를 해결할 것입니다.
goodTree :: String -> Tree String -> Bool
특히이 제약 조건을 따르는 경우 "양호한"트리를 호출합니다. 시드 값이 주어지면 트리의 루트 노드가 시드의 마지막 글자와 첫 글자가 같은 값을 갖는다면 좋다.
goodTree [] _ = False
goodTree seed Empty = False
goodTree seed (Node "" _) = False
goodTree seed (Node (h:_) _) = last seed == h
우리는 단순히이 선정시
makeTree seed vals = Node seed children where
children =
filter goodTree
$ map (\(before, here, after) -> makeTree here (before ++ after))
$ selectEach
$ vals
그리고 지금 우리가 완료에 따라 아이들을 필터링 할 수 있습니다!
> makeTree "sip" ["sour","piss","rune","profit","today","rat"]
Node "sip"
[ Node "piss" [ Node "sour" [ Node "rune" []
, Node "rat" [ Node "today" [] ]
]
]
, Node "profit" [ Node "today" [] ]
]
전체 코드는 다음과 같습니다
selectEach :: [a] -> [([a], a, [a])]
selectEach [] = []
selectEach (a:as) = go ([], a, as) where
go (before, here, []) = [(before, here, [])]
go (before, here, [email protected](a:as)) = (before, here, after) : go (here:before, a, as)
data Tree a = Empty | Node a [Tree a] deriving Show
goodTree :: Eq a => [a] -> Tree [a] -> Bool
goodTree [] _ = False
goodTree seed Empty = False
goodTree seed (Node [] _) = False
goodTree seed (Node (h:_) _) = last seed == h
makeTree :: Eq a => [a] -> [[a]] -> Tree [a]
makeTree seed vals = Node seed children where
children =
filter (goodTree seed)
$ map (\(before, here, after) -> makeTree here (before ++ after))
$ selectEach
$ vals
그리고 selectEach
이 목록 지퍼이라고 무엇을 makeTree
가 Reader
모나드에서 작동하는 방법을 사용하는 방법에 독서 가치가있을 것입니다. 둘 다 내가 여기서 사용했던 방법을 응고시키는 중간 주제입니다.
재미있는 - 내가 접근 ... 세트 사용에 관한 생각했다 방법 완전히 다른,이 목록을 필터링하는 것보다 더 효율적 아닌가요? – beoliver
비록 꽤 자주 세트를 필터링한다고 가정합니다.) – beoliver
세트가 더 효율적일 수 있습니다.이 경우에는 효율성이 필요하지 않습니다. 시드의 각 선택에 대해 한 번 남은 후보 단어의 각 목록을 통과합니다. 또한 게으름으로 인해 나무가 얼마나 많이 펼쳐지는지주의 깊게 메모 할 필요가 있습니다.모든 경우에 너무 일찍 최적화하면 정확성에 필요한 점수가 흐려질 수 있습니다. –