!!
(이는 어쨌든 0
-indexed입니다)은 보통 no-go입니다. 그것은 매우 "필수적인"일이며, 여기에서와 같이 항상 일정한 지수로 악취가납니다. 즉, 패턴 일치가 실제로 필요합니다. 또한 색인에서 목록 (type String = [Char]
)을 분리하고 두 부분으로 나누어 조작하는 것은 나쁜 아이디어입니다. 이러한 목록은 연결되어 있고 불변이므로 첫 번째 부분 전체를 복사하게됩니다. 문자열이 비어
- 경우, 실패 다음과 같이
당신이하고 싶은 방법입니다.
*
으로 시작하는 경우 어떻게 든 왼쪽 하위 트리를 구문 분석하고 한 단계에서 나머지 목록을 가져온 다음 나머지 부분 중 올바른 하나를 구문 분석하여 Branch
을 만듭니다.
- 다른 문자 인 경우
Leaf
으로 만드십시오.
문자열을 분할 할 위치를 파악하고 실제로 문자열을 분할 한 다음 절반을 구문 분석 할 필요가 없습니다. 당신이 더 이상 할 수 없게 될 때까지 목록을 분석하고, 남은 것이 무엇이든 (또는 내가 올바르게 말할까요?) 다시 파싱 될 수 있습니다.
그래서 : 함수 Trie
에 String
의 시작을 소비하고 뒤에 구문 분석되지 않은 비트 잎 (그리고 그냥 구문 분석하는 데 실패 할 경우 Nothing
을 제공합니다) constructTrie' :: String -> Maybe (Trie, String)
을 정의합니다. 이 함수는 재귀 적입니다. 따라서 추가 출력 값을 얻을 수 있습니다. 목록의 나머지 부분을 이동하려면 여분의 배관이 필요합니다. constructTrie
자체는 다음 래퍼로 정의 할 수있다 :
-- Maybe Trie because it's perfectly possible that the String just won't parse
constructTrie :: String -> Maybe Trie
constructTrie s = do (t, "") <- constructTrie' s
-- patmat failure in a monad calls fail; fail @Maybe _ = Nothing
return t
-- can make this local to constructTrie in a where clause
-- or leave it exposed in case it's useful
constructTrie' :: String -> Maybe (Trie, String)
constructTrie' "" = Nothing -- can't parse something from nothing!
constructTrie' ('*':leaves) = do (ln, rs) <- constructTrie' leaves
-- Parse out left branch and leave the right part
-- untouched. Doesn't copy the left half
(rn, rest) <- constructTrie' rs
-- Parse out the right branch. Since, when parsing
-- "**ABC", the recursion will end up with
-- constructTrie' "*ABC", we should allow the slop.
return (Branch ln rn, rest)
constructTrie' (x:xs) = return (Leaf x, xs)
이것은 매우 일반적인 패턴 : 어떤 상태를 해결 통과 별도 배관으로 재귀 함수를 정의하고, 더 좋은 하나에 배치. 나는 명령형 루프가 대개 그들의 상태를 유지하기 위해 변수를 돌연변이시키는 방법에 해당한다고 생각합니다.
이것은 좋은 접근 방법입니다. 문자열 상태를 전달하지 않아도되기 위해 더 멀리 나아가서 'StateT String Maybe Trie'를 사용할 수도 있습니다. – chi