jbapple의 훌륭한 답변에 Riffing에 관한 replicate
하지만, 사용 replicateA
(하는 replicate
대신에, 나는 다음과 같은 내놓았다에 내장되어 있습니다) :
--Unlike fromList, one needs the length explicitly.
myFromList :: Int -> [b] -> Seq b
myFromList l xs = flip evalState xs $ Seq.replicateA l go
where go = do
(y:ys) <- get
put ys
return y
myFromList
(약간보다 효율적인 버전에서)는 이미 정의되어Data.Sequence
있으며 일종의 결과 인 핑거 트리를 구성하기 위해 내부적 으로 사용됩니다 .
일반적으로 직관 replicateA
은 간단합니다. applicativeTree 함수 replicateA
위에 구축되었습니다 . size의 나무 조각 을 가져 와서 이것의 사본을 포함하는 균형 잡힌 나무를 만듭니다. 의 경우 8에서 (하나 의 손가락)이 하드 코딩되어있다. 이 이상의 것은 재귀 적으로 호출됩니다. "적용 적"요소는 단순히 상기 코드의 경우와 같은 상태를 통해 스레딩 효과를 갖는 트리의 구성을 인터리빙한다는 것이다.applicativeTree
m
n
n
Deep
go
복제 된 함수는 단순히 현재 상태를 가져 와서 요소를 맨 위에 표시하고 나머지를 대체하는 작업입니다. 따라서 각 호출에서 입력으로 제공된 목록을 한 단계 더 내려갑니다.
좀 더 구체적인 메모
main = print (length (show (Seq.fromList [1..10000000::Int])))
몇 가지 간단한 테스트에서 이로 인해 흥미로운 성능 트레이드 오프가 발생했습니다. 위의 주요 기능은 myFromList보다 약 1/3 낮았습니다 fromList
. 반면, myFromList
2MB의 상수 힙을 fromList
사용 하고 표준 은 최대 926MB를 사용했습니다. 926MB는 전체 목록을 한 번에 메모리에 저장해야 할 때 발생합니다. 한편, 솔루션 myFromList
은 지연 스트리밍 방식으로 구조를 소비 할 수 있습니다. 속도 문제는 myFromList
(모나드 모나드의 쌍 구성 / 파괴 결과) 약 2 배의 할당을 수행해야 한다는 사실에서 비롯 됩니다.fromList
. CPS 변환 상태 모나드로 이동하여 이러한 할당을 제거 할 수 있지만 게으름을 상실하면 비 스트리밍 방식으로 목록을 통과해야하기 때문에 주어진 시간에 훨씬 더 많은 메모리를 보유하게됩니다.
반면에 전체 시퀀스를 쇼로 강제하지 않고 헤드 또는 마지막 요소를 추출하여 myFromList
즉시 더 큰 승리를 제시하면 헤드 요소를 추출하는 것이 거의 즉각적이며 마지막 요소를 추출하는 것은 0.8입니다. . 한편, 표준 fromList
에서는 헤드 또는 마지막 요소를 추출하는 데 ~ 2.3 초가 소요됩니다.
이것은 모든 세부 사항이며 순도와 게으름의 결과입니다. 돌연변이와 무작위 접근이 가능한 상황에서는 replicate
해결책이 엄청나게 더 좋다고 생각합니다 .
그러나, 다시 할 수있는 방법이 있는지 여부의 문제 제기 않는 applicativeTree
등 myFromList
엄격하게 더 효율적입니다. 문제는 적용 작업이 트리와 다른 순서로 실행된다는 것입니다. 그러나 자연스럽게 통과하는 방법이 있지만이 작동 방법이나 해결 방법이 있는지는 완전히 알지 못했습니다.