에서 실제 세계 하스켈 에 제 4 장 기능 프로그래밍 :
foldr로 foldl 작성 :
-- file: ch04/Fold.hs
myFoldl :: (a -> b -> a) -> a -> [b] -> a
myFoldl f z xs = foldr step id xs z
where step x g a = g (f a x)
위의 코드는 저를 많이 혼란스럽게했고 dps 라는 누군가 가 좀 더 명확하게하기 위해 의미있는 이름으로 다시 작성했습니다.
myFoldl stepL zeroL xs = (foldr stepR id xs) zeroL
where stepR lastL accR accInitL = accR (stepL accInitL lastL)
다른 누군가 Jef G는 예제를 제공하고 기본 메커니즘을 단계별로 보여줌으로써 훌륭한 작업을 수행했습니다.
myFoldl (+) 0 [1, 2, 3]
= (foldR step id [1, 2, 3]) 0
= (step 1 (step 2 (step 3 id))) 0
= (step 1 (step 2 (\a3 -> id ((+) a3 3)))) 0
= (step 1 (\a2 -> (\a3 -> id ((+) a3 3)) ((+) a2 2))) 0
= (\a1 -> (\a2 -> (\a3 -> id ((+) a3 3)) ((+) a2 2)) ((+) a1 1)) 0
= (\a1 -> (\a2 -> (\a3 -> (+) a3 3) ((+) a2 2)) ((+) a1 1)) 0
= (\a1 -> (\a2 -> (+) ((+) a2 2) 3) ((+) a1 1)) 0
= (\a1 -> (+) ((+) ((+) a1 1) 2) 3) 0
= (+) ((+) ((+) 0 1) 2) 3
= ((0 + 1) + 2) + 3
그러나 나는 여전히 그것을 완전히 이해할 수 없습니다. 여기 내 질문이 있습니다.
- ID 기능은 무엇입니까? 의 역할은 무엇입니까? 여기에 왜 필요합니까?
- 위의 예에서 id 함수는 람다 함수의 누산기입니까?
- foldr의 프로토 타입은
foldr :: (a -> b -> b) -> b -> [a] -> b
이고 첫 번째 매개 변수는 두 개의 매개 변수가 필요한 함수이지만 myFoldl 구현의 단계 함수는 3 개의 매개 변수를 사용하므로 완전히 혼란 스럽습니다!
답변
몇 가지 설명이 순서대로 있습니다!
ID 기능은 무엇입니까? 의 역할은 무엇입니까? 여기에 왜 필요합니까?
id
은 IS 항등 함수 , id x = x
및 함수와 체인 구축 될 때 제로의 당량으로 사용되는 기능 조성물 , (.)
. Prelude에서 정의 된 것을 찾을 수 있습니다 .
위의 예에서 id 함수는 람다 함수의 누산기입니까?
누산기는 반복 기능 적용을 통해 구축되는 기능입니다. 누산기의 이름을 step
. 원하는 경우 람다로 작성할 수 있습니다.
foldl f a bs = foldr (\b g x -> g (f x b)) id bs a
또는 Graham Hutton이 다음 과 같이 작성했습니다 .
5.1
foldl
운영자이제
suml
예제 에서 일반화하고 값을 결합foldl
하는 함수f
와v
시작 값으로 값을 사용하여 왼쪽에서 오른쪽 순서로 목록의 요소를 처리 하는 표준 연산자 를 고려해 보겠습니다 .
foldl :: (β → α → β) → β → ([α] → β) foldl f v [ ] = v foldl f v (x : xs) = foldl f (f v x) xs
이 연산자를 사용하면
suml
간단히suml = foldl (+) 0
. 다른 많은 함수는를 사용하여 간단한 방법으로 정의 할 수 있습니다foldl
. 예를 들어, 다음과 같이 표준 함수reverse
를 재정의 할 수 있습니다foldl
.
reverse :: [α] → [α] reverse = foldl (λxs x → x : xs) [ ]
이 정의는
(++)
목록에 비효율적 인 추가 연산자 를 사용하지 않기 때문에 접기를 사용하는 원래 정의보다 효율적 입니다.함수에 대해 이전 섹션에서 계산의 간단한 일반화
suml
함수를 재정의하는 방법을foldl
환산fold
:
foldl f v xs = fold (λx g → (λa → g (f a x))) id xs v
반대로, 목록 인수의 꼬리 부분은 엄격하지만 그렇지 않은 사실 때문에의
fold
관점 에서 재정의 할 수 없습니다. 및 에 관한 유용한 ‘이중성 정리’ 가 많이 있으며 특정 응용 프로그램에 가장 적합한 연산자를 결정하기위한 몇 가지 지침도 있습니다 (Bird, 1998).foldl
foldl
fold
fold
foldl
foldr의 프로토 타입은 foldr :: (a-> b-> b)-> b-> [a]-> b
하스켈 프로그래머는 것을 말할 것입니다 타입 의가 foldr
있다 (a -> b -> b) -> b -> [a] -> b
.
첫 번째 매개 변수는 두 개의 매개 변수가 필요한 함수이지만 myFoldl 구현의 단계 함수는 3 개의 매개 변수를 사용하므로 완전히 혼란 스럽습니다.
이것은 혼란스럽고 마술 적입니다! 우리는 속임수를 사용하고 누산기를 함수로 대체합니다. 그러면 결과를 산출하기 위해 초기 값에 적용됩니다.
그레이엄 허튼 설정하는 트릭 설명 foldl
에 foldr
위의 문서에 있습니다. 다음과 같은 재귀 적 정의를 작성하는 것으로 시작합니다 foldl
.
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f v [] = v
foldl f v (x : xs) = foldl f (f v x) xs
그런 다음에 대한 정적 인수 변환을 통해 리팩터링합니다 f
.
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f v xs = g xs v
where
g [] v = v
g (x:xs) v = g xs (f v x)
이제 안쪽으로 g
플로팅하도록 다시 작성해 보겠습니다 v
.
foldl f v xs = g xs v
where
g [] = \v -> v
g (x:xs) = \v -> g xs (f v x)
이것은 g
함수를 반환하는 하나의 인수의 함수 로 생각하는 것과 같습니다.
foldl f v xs = g xs v
where
g [] = id
g (x:xs) = \v -> g xs (f v x)
이제 우리는 g
리스트를 재귀 적으로 걷는 함수를 가지고 있고, 어떤 함수를 적용합니다 f
. 최종 값은 식별 함수이며 각 단계는 함수도 생성합니다.
그러나 , 우리는 이미 목록에 매우 유사한 재귀 함수를 가지고 있습니다 foldr
.!
2 접기 연산자
fold
를 사용하는 동안 작업자는, 재귀 이론 (Kleene, 1952)에서 그 기원을 가지고fold
프로그래밍 언어 날짜의 중심 개념으로는 APL의 감소 연산자 (아이버슨, 1962)에 백업하고, 나중에 FP의 삽입 운영자에 (배 커스를 , 1978). Haskell에서fold
목록 연산자는 다음과 같이 정의 할 수 있습니다.
fold :: (α → β → β) → β → ([α] → β) fold f v [ ] = v fold f v (x : xs) = f x (fold f v xs)
즉,이 함수 주어진
f
유형α → β → β
및 값v
유형을β
상기 함수
fold f v
유형의리스트를 처리하는[α]
타입의 값을 수득β
NIL로 생성자를 대체함으로써[]
값으로 목록의 마지막에v
, 각각의 단점 생성자(:)
목록 내의하여 기능f
. 이러한 방식으로fold
연산자는 목록 처리를위한 간단한 재귀 패턴을 캡슐화합니다. 여기서 목록에 대한 두 생성자는 단순히 다른 값과 함수로 대체됩니다. 목록에서 익숙한 많은 함수는fold
.
이것은 우리 g
함수 와 매우 유사한 재귀 체계처럼 보입니다 . 이제 트릭 : 사용 가능한 모든 마법 (일명 Bird, Meertens 및 Malcolm)을 사용 하여 fold 의 보편적 속성 인 특수 규칙을 적용합니다.이 규칙 은 g
목록을 처리 하는 함수에 대한 두 정의 간의 동등성입니다 .
g [] = v g (x:xs) = f x (g xs)
경우에만
g = fold f v
따라서 폴드의 보편적 인 속성은 다음과 같습니다.
g = foldr k v
여기서 g
몇 가지를 들어,이 방정식에 해당해야합니다 k
및 v
:
g [] = v
g (x:xs) = k x (g xs)
이전 foldl 디자인에서 우리는 v == id
. 두 번째 방정식의 경우 다음 의 정의 를 계산 해야합니다 k
.
g (x:xs) = k x (g xs)
<=> g (x:xs) v = k x (g xs) v -- accumulator of functions
<=> g xs (f v x) = k x (g xs) v -- definition of foldl
<= g' (f v x) = k x g' v -- generalize (g xs) to g'
<=> k = \x g' -> (\a -> g' (f v x)) -- expand k. recursion captured in g'
계산 된 정의 k
와 v
foldl의 정의를 다음과 같이 대체하면 다음과 같습니다.
foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f v xs =
foldr
(\x g -> (\a -> g (f v x)))
id
xs
v
recursive g
는 foldr combinator로 대체되고 accumulator는 f
역순으로 목록의 각 요소에서 구성 체인을 통해 빌드 된 함수가됩니다 (따라서 오른쪽 대신 왼쪽으로 접습니다).
이것은 확실히 다소 진보 된 것이므로 , 변형을 가능하게하는 fold 의 보편적 인 속성 인 이 변형을 깊이 이해하기 위해 아래 링크 된 Hutton의 튜토리얼을 추천합니다.
참고 문헌
- Haskell Wiki : Foldl as foldr
- 접기의 보편성과 표현성에 대한 튜토리얼 , Graham Hutton, J. Functional Programming 9 (4) : 355-372, 1999 년 7 월.
- Malcolm, G. 대수 데이터 유형 및 프로그램 변환. , Groningen University 박사 논문.
답변
다음 유형을 고려하십시오 foldr
.
foldr :: (b -> a -> a) -> a -> [b] -> a
유형 반면으로는 step
같은 것입니다 b -> (a -> a) -> a -> a
. 단계가로 전달 foldr
되었으므로이 경우 폴드의 유형이 (b -> (a -> a) -> (a -> a)) -> (a -> a) -> [b] -> (a -> a)
.
a
다른 서명 의 다른 의미로 혼동하지 마십시오 . 유형 변수 일뿐입니다. 또한, 그래서 함수의 화살표를 잘 연관 있음을 알아 두셔야 a -> b -> c
과 같은 일이다 a -> (b -> c)
.
예,의 누산기 값 은 유형 foldr
의 함수 이고 a -> a
초기 값은 id
입니다. 이것은 id
아무 일도하지 않는 함수 이기 때문에 의미 가 있습니다. 목록의 모든 값을 추가 할 때 초기 값으로 0으로 시작하는 것과 같은 이유입니다.
step
세 가지 인수 를 취하려면 다음과 같이 다시 작성하십시오.
step :: b -> (a -> a) -> (a -> a)
step x g = \a -> g (f a x)
무슨 일이 일어나고 있는지 더 쉽게 볼 수 있습니까? 함수를 반환하기 때문에 추가 매개 변수가 필요하며이를 작성하는 두 가지 방법은 동일합니다. 또한 후 추가 매개 변수를 참고 foldr
: (foldr step id xs) z
. 괄호 안의 부분은 폴드 자체로 함수를 반환 한 다음에 적용됩니다 z
.
답변
(내 대답 [1] , [2] , [3] , [4] 을 빠르게 훑어 보면서 Haskell의 구문, 고차 함수, 커링, 함수 구성, $ 연산자, 중위 / 접두사 연산자, 섹션 및 람다를 이해했는지 확인하십시오. )
접힘의 보편적 인 속성
배는 재귀의 특정 종류의 단지 법전 편찬이다. 그리고 보편성 속성은 단순히 재귀가 특정 형식을 따르면 일부 공식 규칙에 따라 폴드로 변환 될 수 있음을 나타냅니다. 반대로 모든 폴드는 그런 종류의 재귀로 변형 될 수 있습니다. 다시 한 번, 일부 재귀는 정확히 동일한 답을 제공하는 접기로 변환 될 수 있지만 일부 재귀는 불가능하며이를 수행하는 정확한 절차가 있습니다.
기본적으로 재귀 함수가 목록에서 작동하면 왼쪽 과 같이 보입니다. , 하나에게 폴드를 변환 할 수 있는 권리를 대체 f
하고 v
거기에 실제로이 무엇인지에 대해.
g [] = v ⇒
g (x:xs) = f x (g xs) ⇒ g = foldr f v
예를 들면 :
sum [] = 0 {- recursion becomes fold -}
sum (x:xs) = x + sum xs ⇒ sum = foldr 0 (+)
여기에 v = 0
와 sum (x:xs) = x + sum xs
동일 sum (x:xs) = (+) x (sum xs)
하므로f = (+)
. 2 개 더보기
product [] = 1
product (x:xs) = x * product xs ⇒ product = foldr 1 (*)
length [] = 0
length (x:xs) = 1 + length xs ⇒ length = foldr (\_ a -> 1 + a) 0
운동:
구현
map
,filter
,reverse
,concat
및concatMap
재귀 막에 상기와 같은 기능을 왼쪽 측면.foldr 이러한 변환 함수 5 위 식에 따라 대체하고,
f
그리고v
온 스크롤 식 오른쪽 .
foldr를 통한 Foldl
왼쪽에서 오른쪽으로 숫자를 합산하는 재귀 함수를 작성하는 방법은 무엇입니까?
sum [] = 0 -- given `sum [1,2,3]` expands into `(1 + (2 + 3))`
sum (x:xs) = x + sum xs
찾기 위해 오는 첫 번째 재귀 함수는 합산을 시작하기 전에 완전히 확장됩니다. 그것은 우리가 필요로하는 것이 아닙니다. 한 가지 접근 방식은 accumulator 가있는 재귀 함수를 만드는 것입니다.이 함수는 각 단계에서 즉시 숫자를 더합니다 ( 꼬리 재귀에 대해 읽어보십시오. 재귀 전략에 대해 자세히 알아 에 대해 ).
suml :: [a] -> a
suml xs = suml' xs 0
where suml' [] n = n -- auxiliary function
suml' (x:xs) n = suml' xs (n+x)
좋아, 그만! GHCi에서이 코드를 실행하고 작동 방식을 이해했는지 확인한 다음 신중하고 신중하게 진행하십시오. suml
접기로 재정의 할 수는 없지만 그럴 suml'
수 있습니다.
suml' [] = v -- equivalent: v n = n
suml' (x:xs) n = f x (suml' xs) n
suml' [] n = n
함수 정의에서 그렇죠? 그리고 v = suml' []
보편적 속성 공식에서. 함께 이것은 v n = n
수신하는 모든 것을 즉시 반환하는 함수를 제공합니다 v = id
. 계산해 봅시다 f
.
suml' (x:xs) n = f x (suml' xs) n
-- expand suml' definition
suml' xs (n+x) = f x (suml' xs) n
-- replace `suml' xs` with `g`
g (n+x) = f x g n
따라서 suml' = foldr (\x g n -> g (n+x)) id
, 따라서, suml = foldr (\x g n -> g (n+x)) id xs 0
.
foldr (\x g n -> g (n + x)) id [1..10] 0 -- return 55
이제 일반화 +
하고 변수 함수로 대체 하면됩니다.
foldl f a xs = foldr (\x g n -> g (n `f` x)) id xs a
foldl (-) 10 [1..5] -- returns -5
결론
이제 Graham Hutton의 fold의 보편성과 표현력에 대한 튜토리얼을 읽어 보세요. 펜과 종이를 가져 와서 대부분의 접힌 부분을 스스로 얻을 때까지 그가 쓰는 모든 것을 파악하십시오. 무언가 이해하지 못한다고 땀을 흘리지 마십시오. 언제든지 나중에 돌아올 수 있지만 미루지 마십시오.
답변
함수가 소개하는 스파게티라는 이름을 제외하고는 foldl
으로 표현할 수있는 내 증명이 있습니다 .foldr
step
명제는 다음 foldl f z xs
과 같습니다.
myfoldl f z xs = foldr step_f id xs z
where step_f x g a = g (f a x)
여기서 주목해야 할 첫 번째 중요한 것은 첫 번째 줄의 오른쪽이 실제로 다음과 같이 평가된다는 것입니다.
(foldr step_f id xs) z
foldr
세 개의 매개 변수 만 취하기 때문 입니다. 이것은 이미 foldr
값이 아니라 카레 함수를 계산 한 다음에 적용됨을 암시합니다 z
. 다음과 같은지 확인하기 위해 조사해야 할 두 가지 사례 myfoldl
가 있습니다 foldl
.
-
기본 케이스 : 빈 목록
myfoldl f z [] = foldr step_f id [] z (by definition of myfoldl) = id z (by definition of foldr) = z foldl f z [] = z (by definition of foldl)
-
비어 있지 않은 목록
myfoldl f z (x:xs) = foldr step_f id (x:xs) z (by definition of myfoldl) = step_f x (foldr step_f id xs) z (-> apply step_f) = (foldr step_f id xs) (f z x) (-> remove parentheses) = foldr step_f id xs (f z x) = myfoldl f (f z x) xs (definition of myfoldl) foldl f z (x:xs) = foldl f (f z x) xs
2.에서 첫 번째 줄과 마지막 줄은 두 경우 모두 동일한 형식을 갖기 때문에 목록을으로 접는 데 사용할 수 있습니다 xs == []
.이 경우 1은 동일한 결과를 보장합니다. 그래서 귀납법으로 myfoldl == foldl
.
답변
수학에 대한 왕도는 없으며 Haskell을 통해서도 없습니다. 허락하다
h z = (foldr step id xs) z where
step x g = \a -> g (f a x)
도대체 무엇입니까 h z
? 그 가정 xs = [x0, x1, x2]
.
폴더 정의 적용 :
h z = (step x0 (step x1 (step x2 id))) z
단계 정의 적용 :
= (\a0 -> (\a1 -> (\a2 -> id (f a2 x2)) (f a1 x1)) (f a0 x0)) z
람다 함수로 대체합니다.
= (\a1 -> (\a2 -> id (f a2 x2)) (f a1 x1)) (f z x0)
= (\a2 -> id (f a2 x2)) (f (f z x0) x1)
= id (f (f (f z x0) x1) x2)
ID 정의 적용 :
= f (f (f z x0) x1) x2
foldl 정의 적용 :
= foldl f z [x0, x1, x2]
로열로드인가요?
답변
투표하기 전에 다음 단락을 읽으십시오.
이 접근 방식이 자신의 사고 방식에 더 적합하다고 생각하는 사람들을 위해 답변을 게시하고 있습니다. 대답에는 중복 된 정보와 생각이 포함될 수 있지만 문제를 해결하기 위해 필요한 것입니다. 또한 이것은 같은 질문에 대한 또 다른 답변이기 때문에 다른 답변과 상당한 겹침이 있음이 분명하지만이 개념을 어떻게 이해할 수 있었는지 이야기합니다.
사실 나는이 주제를 이해하려고 노력하면서 내 생각에 대한 개인적인 기록으로이 노트를 쓰기 시작했다. 내가 정말로 그것을 얻었다면 내가 그것의 핵심을 만지는 데 하루 종일 걸렸다.
이 간단한 연습을 이해하는 나의 먼 길
쉬운 부분 : 무엇을 결정해야합니까?
다음 예제 호출은 어떻게 되나요?
foldl f z [1,2,3,4]
다음 다이어그램으로 시각화 할 수 있습니다 ( Wikipedia 에 있지만 처음에는 다른 답변 에서 보았습니다 ).
_____results in a number
/
f f (f (f (f z 1) 2) 3) 4
/ \
f 4 f (f (f z 1) 2) 3
/ \
f 3 f (f z 1) 2
/ \
f 2 f z 1
/ \
z 1
(부수적으로,의 foldl
각 응용 프로그램을 사용할 때 f
수행되지 않고 표현은 위에서 작성한 방식으로 썽킹됩니다. 원칙적으로 맨 아래로 갈 때 계산할 수 있으며 정확히 foldl'
수행됩니다.)
운동은 기본적으로 사용하는 우리에게 도전 foldr
하는 대신 foldl
적절하게 (우리가 사용하도록 단계의 기능을 변경하여 s
대신 f
(우리가 사용하므로) 초기 축적 ?
대신에 z
); 목록은 동일하게 유지됩니다. 그렇지 않으면 무엇에 대해 이야기하고 있습니까?
에 대한 호출 foldr
은 다음과 같아야합니다.
foldr s ? [1,2,3,4]
해당 다이어그램은 다음과 같습니다.
_____what does the last call return?
/
s
/ \
1 s
/ \
2 s
/ \
3 s
/ \
4 ? <--- what is the initial accumulator?
호출 결과
s 1 (s 2 (s 3 (s 4 ?)))
무엇 s
과 ?
? 그리고 그들의 유형은 무엇입니까? 것 같습니다 s
훨씬처럼 두 개의 인수 기능입니다 f
,하지만의 결론으로 점프하지 말자. 또한 ?
잠시 잠시 놔두고 작동 z
이 시작 되 자마자 작동 해야하는 것을 관찰합시다 1
. 그러나 z
아마도 두 인수 s
함수에 대한 호출, 즉 호출에서 s 1 (…)
어떻게 작용할 수 있습니까? 우리는 s
앞서 언급 한 2 개가 아닌 3 개의 인수를받는 an 을 선택하여 수수께끼의이 부분을 해결할 수 있습니다 . 그래서 가장 바깥 쪽 호출 s 1 (…)
은 우리가 전달할 수있는 하나의 인수를 취하는 함수가 될 z
것입니다!
이것은 우리가 원래 호출을 원한다는 것을 의미합니다.
f (f (f (f z 1) 2) 3) 4
~와 동등하다
s 1 (s 2 (s 3 (s 4 ?))) z
즉, 부분적으로 적용된 함수를 원합니다.
s 1 (s 2 (s 3 (s 4 ?)))
다음 람다 함수와 동일합니다.
(\z -> f (f (f (f z 1) 2) 3) 4)
다시 말하지만, 필요한 “유일한”조각은 s
및 ?
입니다.
전환점 : 기능 구성 인식
이전 다이어그램을 다시 그리고 오른쪽에 각 호출 s
이 동일하게 하려는 내용을 작성해 봅시다 .
s s 1 (…) == (\z -> f (f (f (f z 1) 2) 3) 4)
/ \
1 s s 2 (…) == (\z -> f (f (f z 2) 3) 4)
/ \
2 s s 3 (…) == (\z -> f (f z 3) 4)
/ \
3 s s 4 ? == (\z -> f z 4)
/ \
4 ? <--- what is the initial accumulator?
나는 다이어그램의 구조 (…)
에서 각 선이 그 아래 선의 오른쪽에 있다는 것이 분명하기를 바랍니다 . 더 좋은 것은 이전 (아래) 호출에서 반환 된 함수입니다.s
.
또한 s
with 인수 x
및에 대한 호출 이의 유일한 인수 에 대한 부분 적용에 y
대한 (전체) 응용 프로그램 임을 분명히해야합니다 . to 의 부분적 적용은 람다로 작성 될 수 있기 때문에 , 완전히 적용 하면 람다 가됩니다.이 경우에는 다음과 같이 다시 작성합니다 . 에 대한 식으로 단어를 번역 우리가 얻을y
f
x
f
x
(\z -> f z x)
y
(\z -> y (f z x))
y . (\z -> f z x)
s
s x y = y . (\z -> f z x)
(이것은 s x y z = y (f z x)
변수의 이름을 바꾸면 책과 동일한,과 동일합니다.)
마지막 비트 : 누산기 의 초기 “값” ?
은 무엇입니까? 위 다이어그램은 중첩 된 호출을 확장하여 컴포지션 체인으로 만들어 다시 작성할 수 있습니다.
s s 1 (…) == (\z -> f z 4) . (\z -> f z 3) . (\z -> f z 2) . (\z -> f z 1)
/ \
1 s s 2 (…) == (\z -> f z 4) . (\z -> f z 3) . (\z -> f z 2)
/ \
2 s s 3 (…) == (\z -> f z 4) . (\z -> f z 3)
/ \
3 s s 4 ? == (\z -> f z 4)
/ \
4 ? <--- what is the initial accumulator?
우리는 여기서 s
단순히의 연속적인 부분적 적용을 “더미 쌓아 올린다” 는 것을 f
알지만, y
in s x y = y . (\z -> f z x)
은의 해석이s 4 ?
(그리고 다른 모든 것)이 맨 왼쪽 람다로 구성 될 선행 함수를 놓친다는 .
그것은 우리의 ?
기능 일뿐입니다.. 에 대한 호출에서 자리를 차지하는 것 외에도 존재 이유를 제공 할 때 foldr
입니다. 결과 함수를 변경하지 않기 위해 무엇을 선택할 수 있습니까? 답 : id
, 구성 연산자에 대한 식별 요소(.)
.
s s 1 (…) == id . (\z -> f z 4) . (\z -> f z 3) . (\z -> f z 2) . (\z -> f z 1)
/ \
1 s s 2 (…) == id . (\z -> f z 4) . (\z -> f z 3) . (\z -> f z 2)
/ \
2 s s 3 (…) == id . (\z -> f z 4) . (\z -> f z 3)
/ \
3 s s 4 id == id . (\z -> f z 4)
/ \
4 id
그래서 추구하는 기능은
myFoldl f z xs = foldr (\x g a -> g (f a x)) id xs z
답변
도움이 될 수 있습니다. 다른 방식으로 확장 해 보았습니다.
myFoldl (+) 0 [1,2,3] =
foldr step id [1,2,3] 0 =
foldr step (\a -> id (a+3)) [1,2] 0 =
foldr step (\b -> (\a -> id (a+3)) (b+2)) [1] 0 =
foldr step (\b -> id ((b+2)+3)) [1] 0 =
foldr step (\c -> (\b -> id ((b+2)+3)) (c+1)) [] 0 =
foldr step (\c -> id (((c+1)+2)+3)) [] 0 =
(\c -> id (((c+1)+2)+3)) 0 = ...