어떻게 fold
다른가가 혼란의 빈번한 원인 인 것처럼 보이므로 다음은보다 일반적인 개요입니다.
[x1, x2, x3, x4 ... xn ]
일부 함수 f
및 seed를 사용 하여 n 값 목록을 접는 것을 고려하십시오 z
.
foldl
is :
- 왼쪽 연관 :
f ( ... (f (f (f (f z x1) x2) x3) x4) ...) xn
- Tail recursive : 목록을 반복하여 나중에 값을 생성합니다.
- Lazy : 결과가 필요할 때까지 아무것도 평가되지 않습니다.
- 뒤로 :
foldl (flip (:)) []
목록을 반대로합니다.
foldr
is :
- 오른쪽 연관 :
f x1 (f x2 (f x3 (f x4 ... (f xn z) ... )))
- 인수로 재귀 : 각 반복
f
은 다음 값과 나머지 목록을 접은 결과에 적용됩니다 .
- Lazy : 결과가 필요할 때까지 아무것도 평가되지 않습니다.
- Forwards :
foldr (:) []
변경되지 않은 목록을 반환합니다.
약간 미묘한 점은 여기에있다 가끔 여행 사람들까지 그 : 때문에 foldl
입니다 뒤쪽 의 각 응용 프로그램 f
받는 추가 외부 결과; lazy 이므로 결과가 필요할 때까지 아무것도 평가되지 않습니다. 즉, 결과의 일부를 계산하기 위해 Haskell은 먼저 중첩 된 함수 응용 프로그램의 표현식을 구성하는 전체 목록을 반복 한 다음 가장 바깥 쪽 함수를 평가하고 필요에 따라 인수를 평가합니다. f
항상 첫 번째 인수를 사용하는 경우 Haskell이 가장 안쪽의 용어까지 계속 반복 한 다음의 각 응용 프로그램을 역으로 계산해야 함을 의미 f
합니다.
이것은 분명히 대부분의 기능적인 프로그래머가 알고 사랑하는 효율적인 꼬리 재귀와는 거리가 멀다!
사실 foldl
기술적으로는 꼬리 재귀 적이 지만 , 모든 결과를 평가하기 전에 전체 결과 표현식이 작성되기 때문에 foldl
스택 오버플로가 발생할 수 있습니다!
반면에 foldr
. 또한 게으르지 만 앞으로 실행되기 때문에의 각 응용 프로그램 이 결과 내부 에 f
추가됩니다 . 따라서 결과를 계산하기 위해 Haskell은 단일 함수 응용 프로그램을 구성하며 두 번째 인수는 나머지 접힌 목록입니다. 경우 데이터 생성자, 인스턴스 - - 두번째 인수 지연되는 결과가 될 것이다 증분 지연 계산할 접어서 각각의 단계, 때만 평가된다 필요로하는 결과의 일부.f
그래서 우리는 왜 foldr
가끔 무한 목록에서 작동 하는지 알 수 있습니다 foldl
. 전자는 무한 목록을 다른 지연 무한 데이터 구조로 느리게 변환 할 수있는 반면 후자는 결과의 일부를 생성하기 위해 전체 목록을 검사해야합니다. 반면에 foldr
,와 같이 즉시 두 인수를 모두 필요로하는 함수를 사용하면 (+)
, 작동 (또는 작동하지 않음)과 매우 유사하게 foldl
평가하기 전에 거대한 표현식을 작성합니다.
따라서 주목해야 할 두 가지 중요한 사항은 다음과 같습니다.
foldr
지연 재귀 데이터 구조를 다른 구조로 변환 할 수 있습니다.
- 그렇지 않으면 지연 접기가 크거나 무한한 목록에서 스택 오버플로와 함께 충돌합니다.
당신은 그것이 foldr
할 수있는 모든 것을 foldl
할 수있는 것처럼 들리는 것을 알았을 것입니다 . 사실입니다! 사실, foldl은 거의 쓸모가 없습니다!
하지만 무한이 아닌 큰 목록을 접어서 지연되지 않은 결과를 생성하려면 어떻게해야할까요? 이를 위해 표준 라이브러리가 제공 하는 strict fold를 원합니다 .
foldl'
is :
- 왼쪽 연관 :
f ( ... (f (f (f (f z x1) x2) x3) x4) ...) xn
- Tail recursive : 목록을 반복하여 나중에 값을 생성합니다.
- 엄격함 : 각 기능 응용 프로그램은 그 과정에서 평가됩니다.
- 뒤로 :
foldl' (flip (:)) []
목록을 반대로합니다.
때문에 foldl'
입니다 엄격한 , 하스켈 것입니다 결과를 계산하는 평가 f
대신 왼쪽 인수를시키는의 각 단계에서 거대한, 평가되지 않은 표현을 축적. 이것은 우리가 원하는 일반적이고 효율적인 꼬리 재귀를 제공합니다! 다시 말해:
foldl'
큰 목록을 효율적으로 접을 수 있습니다.
foldl'
무한 목록에서 무한 루프 (스택 오버플로가 발생하지 않음)에서 중단됩니다.
Haskell 위키에도 이에 대해 논의하는 페이지 가 있습니다.
foldr
더 나은보다foldl
에 하스켈 반대의 사실이지만, 얼랑 (I 전에 배운 하스켈 ). 이후 얼랑 게으른하지 않고 기능이되지 않습니다 카레 그래서foldl
에서 얼랑 같은 동작합니다foldl'
위. 이것은 훌륭한 대답입니다! 잘하셨습니다. 감사합니다!