이 예제를 단순화하여 어떤 일이 발생하는지 살펴 보겠습니다. 게으른 평가와 그래프 감소에 대해 생각할 때 그보다 더 낮은 수준으로 갈 필요없이 대부분 알아낼 수 있습니다. 이제이 코드 ourLast (mkList 3)
의 단순화 감소를 살펴 보자 :
ourLast :: [a] -> a
ourLast [] = error "ourLast []"
ourLast (x:[]) = x
ourLast (_:xs) = ourLast xs
mkList :: Int -> [Int]
mkList 0 = []
mkList n = let rest = mkList (n-1) in n : rest
?foo
는 "우리가 아직 못 봤어 값"을 의미한다. 우리는 "let"으로 이것을 생성합니다.
[email protected]
우리가
?foo
을 검사 할 때 , 그것은
[email protected]
foo := bar
이
-- We start here by creating some value and giving it to ourLast to
-- look at.
let ?list3 = mkList 3
ourLast ?list3
-- At this point ourLast examines its argument to figure out whether
-- it's of the form (_:_) or []
-- mkList sees that 3 /= 0, so it can pick the second case, and it
-- computes the value for ?list3.
-- (I'll skip the arithmetic reductions and other boring things.)
let ?list2 = mkList 2
list3 := 3 : ?list2 -- we don't need to compute ?list2 yet, so
-- (mkList 3) is done and we can go back to ourLast
ourLast [email protected](3 : ?list2)
-- ourLast needs to examine ?list2 to find out whether it's [] or not,
-- so mkList does the same thing again
let ?list1 = mkList 1
list2 := 2 : ?list1
ourLast [email protected](3 : [email protected](2 : ?list1))
-- Now ourLast has enough information to continue;
-- ourLast (_ : [email protected](_ : _)) = ourLast xs
-- Notice how we don't need to compute list2 a second time; we save its
-- value the first time we compute it. This is what lazy evaluation is.
ourLast [email protected](2 : ?list1)
-- at this point, there are no references to `list3` anywhere, so it
-- can be GCed.
-- Repeat (ourLast examines ?list1, mkList sees that 1 /= 0).
let ?list0 = mkList 0
list1 := 1 : ?list0
ourLast [email protected](2 : [email protected](1 : ?list0))
ourLast [email protected](1 : ?list0)
-- Can GC list2.
-- Now mkList is being called with 0, so it just returns an empty list.
list0 := []
ourLast [email protected](1 : [email protected][])
1
-- We're done! (And we can GC list1.)
"우리가
foo
가
bar
것을 알아 낸 적이 없다"라는 뜻이됩니다 "우리가 알아 냈어요 우리가 이미 계산 한 값은
bar
은"의미
주어진 시간에 두 개의 썽크를 실제로 할당 할 필요가 있으며 나머지는 아직 계산되지 않았거나 GC 될 수 있습니다. ourLast list3
을 평가할 때 평가는 ourLast
과 mkList
(coroutines와 유사) 사이에서 앞뒤로 이동합니다.
당신은 하스켈 컴파일러의 수준에 작동하는 경향이 방법에 대한보다 정확한 아이디어를 얻고 싶다면 "때 할당 앞으로 일어날 수행 방법", 다음이 도움이됩니다 :
평가 그래프 감소의 관점에서 바로 작동하는 방법 게으른 일반적인 이해 - 예를 들어, this article - 유용합니다.
하스켈 프로파일 링에 대한 자세한 내용은 http://www.haskell.org/haskellwiki/How_to_profile_a_Haskell_program을 참조하십시오. 자세한 메모리 사용을 제어 할 수 있습니다. –
두 번째 코드 스 니펫은 많은 양의 메모리를 소비하지 않습니다. 왜냐하면'map' 및 generator로만 지연 목록에서 작동하기 때문에 주기적으로 순환하면서 한 번에 하나의 항목 만 필요하기 때문입니다. 이전 항목이 필요하지 않으면 가비지 수집됩니다. –