foldr를 사용하여 foldl 작성


79

에서 실제 세계 하스켈 에 제 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

그러나 나는 여전히 그것을 완전히 이해할 수 없습니다. 여기 내 질문이 있습니다.

  1. ID 기능은 무엇입니까? 의 역할은 무엇입니까? 여기에 왜 필요합니까?
  2. 위의 예에서 id 함수는 람다 함수의 누산기입니까?
  3. foldr의 프로토 타입은 foldr :: (a -> b -> b) -> b -> [a] -> b이고 첫 번째 매개 변수는 두 개의 매개 변수가 필요한 함수이지만 myFoldl 구현의 단계 함수는 3 개의 매개 변수를 사용하므로 완전히 혼란 스럽습니다!

2
실제 자학, 위해step = curry $ uncurry (&) <<< (flip f) *** (.)
Weijun 저우

답변:


99

몇 가지 설명이 순서대로 있습니다!

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하는 함수 fv시작 값으로 값을 사용하여 왼쪽에서 오른쪽 순서로 목록의 요소를 처리 하는 표준 연산자 를 고려해 보겠습니다 .

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).foldlfoldlfoldfoldfoldl

foldr의 프로토 타입은 foldr :: (a-> b-> b)-> b-> [a]-> b

하스켈 프로그래머는 것을 말할 것입니다 타입 의가 foldr있다 (a -> b -> b) -> b -> [a] -> b.

첫 번째 매개 변수는 두 개의 매개 변수가 필요한 함수이지만 myFoldl 구현의 단계 함수는 3 개의 매개 변수를 사용하므로 완전히 혼란 스럽습니다.

이것은 혼란스럽고 마술 적입니다! 우리는 속임수를 사용하고 누산기를 함수로 대체합니다. 그러면 결과를 산출하기 위해 초기 값에 적용됩니다.

그레이엄 허튼 설정하는 트릭 설명 foldlfoldr위의 문서에 있습니다. 다음과 같은 재귀 적 정의를 작성하는 것으로 시작합니다 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몇 가지를 들어,이 방정식에 해당해야합니다 kv:

    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'

계산 된 정의 kvfoldl의 정의를 다음과 같이 대체하면 다음과 같습니다.

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의 튜토리얼을 추천합니다.


참고 문헌


1
Plz는 오타를 수정 k = \x g' -> (\a -> g' (f v x)) 하고(\x g -> (\a -> g (f v x)))
Kamel 2015 년

10

다음 유형을 고려하십시오 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.


6

(내 대답 [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 = 0sum (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

운동:

  1. 구현 map, filter, reverse, concatconcatMap 재귀 막에 상기와 같은 기능을 왼쪽 측면.

  2. 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의 보편성과 표현력에 대한 튜토리얼을 읽어 보세요. 펜과 종이를 가져 와서 대부분의 접힌 부분을 스스로 얻을 때까지 그가 쓰는 모든 것을 파악하십시오. 무언가 이해하지 못한다고 땀을 흘리지 마십시오. 언제든지 나중에 돌아올 수 있지만 미루지 마십시오.


나는이 대답이 받아 들여진 대답보다 더 간단하고 명확하다고 생각합니다.
기가 바이트

5

함수가 소개하는 스파게티라는 이름을 제외하고는 foldl으로 표현할 수있는 내 증명이 있습니다 .foldrstep

명제는 다음 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.

  1. 기본 케이스 : 빈 목록

      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)
    
  2. 비어 있지 않은 목록

      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.


2

수학에 대한 왕도는 없으며 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]

로열로드인가요?


2

투표하기 전에 다음 단락을 읽으십시오.

이 접근 방식이 자신의 사고 방식에 더 적합하다고 생각하는 사람들을 위해 답변을 게시하고 있습니다. 대답에는 중복 된 정보와 생각이 포함될 수 있지만 문제를 해결하기 위해 필요한 것입니다. 또한 이것은 같은 질문에 대한 또 다른 답변이기 때문에 다른 답변과 상당한 겹침이 있음이 분명하지만이 개념을 어떻게 이해할 수 있었는지 이야기합니다.

사실 나는이 주제를 이해하려고 노력하면서 내 생각에 대한 개인적인 기록으로이 노트를 쓰기 시작했다. 내가 정말로 그것을 얻었다면 내가 그것의 핵심을 만지는 데 하루 종일 걸렸다.

이 간단한 연습을 이해하는 나의 먼 길

쉬운 부분 : 무엇을 결정해야합니까?

다음 예제 호출은 어떻게 되나요?

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 .

또한 swith 인수 x및에 대한 호출 이의 유일한 인수 에 대한 부분 적용에 y대한 (전체) 응용 프로그램 임을 분명히해야합니다 . to 의 부분적 적용은 람다로 작성 될 수 있기 때문에 , 완전히 적용 하면 람다 가됩니다.이 경우에는 다음과 같이 다시 작성합니다 . 에 대한 식으로 단어를 번역 우리가 얻을yfxfx(\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알지만, yin 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

1

도움이 될 수 있습니다. 다른 방식으로 확장 해 보았습니다.

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 = ...

1
foldr step zero (x:xs) = step x (foldr step zero xs)
foldr _ zero []        = zero

myFold f z xs = foldr step id xs z
  where step x g a = g (f a x)

myFold (+) 0 [1, 2, 3] =
  foldr step id [1, 2, 3] 0
  -- Expanding foldr function
  step 1 (foldr step id [2, 3]) 0
  step 1 (step 2 (foldr step id [3])) 0
  step 1 (step 2 (step 3 (foldr step id []))) 0
  -- Expanding step function if it is possible
  step 1 (step 2 (step 3 id)) 0
  step 2 (step 3 id) (0 + 1)
  step 3 id ((0 + 1) + 2)
  id (((0 + 1) + 2) + 3)

글쎄, 적어도 이것은 나를 도왔다. 그것조차 옳지 않습니다.


실제 시퀀스는 foldr step id [1, 2, 3] 0-> step 1 (foldr step id [2, 3]) 0-> (foldr step id [2, 3]) (0 + 1)-> step 2 (foldr step id [3]) (0 + 1)-> (foldr step id [3]) ((0 + 1) + 2)-> step 3 (foldr step id []) ((0 + 1) + 2)-> (foldr step id []) (((0 + 1) + 2) + 3)-> id (((0 + 1) + 2) + 3)입니다.
Will Ness

0

이 답변은 아래의 정의를 세 단계로 쉽게 이해할 수 있도록합니다.

-- 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)

1 단계 : 함수 평가의 폴드를 함수 조합으로 변환

foldl f z [x1 .. xn] = z & f1 & .. & fn = fn . .. . f1 z. 어느 fi = \z -> f z xi.

(사용 z & f1 & f2 & .. & fn한다는 의미 fn ( .. (f2 (f1 z)) .. )입니다.)

2 단계 : 함수 조합을 foldr방식으로 표현

foldr (.) id [f1 .. fn] = (.) f1 (foldr (.) id [f2 .. fn]) = f1 . (foldr (.) id [f2 .. fn]). 나머지를 펼치면 foldr (.) id [f1 .. fn] = f1 . .. . fn.

순서가 역전 된 것을 알면 역전 된 형태의 (.). 정의한 rc f1 f2 = (.) f2 f1 = f2 . f1다음 foldr rc id [f1 .. fn] = rc f1 (foldr (.) id [f2 .. fn]) = (foldr (.) id [f2 .. fn]) . f1. 나머지를 펼치면 foldr rc id [f1 .. fn] = fn . .. . f1.

3 단계. 함수 목록의 접기를 피연산자 목록의 접기로 변환

찾기 step그 차종을 foldr step id [x1 .. xn] = foldr rc id [f1 .. fn]. 쉽게 찾을 수 step = \x g z -> g (f z x)있습니다.

3 단계에서의 정의 foldl사용은 foldr분명하다 :

  foldl f z xs
= fn . .. . f1 z
= foldr rc id fs z
= foldr step id xs z

정확성 증명 :

foldl f z xs = foldr (\x g z -> g (f z x)) id xs z
             = step x1 (foldr step id [x2 .. xn]) z
             = s1 (foldr step id [x2 .. xn]) z
             = s1 (step x2 (foldr step id [x3 .. xn])) z
             = s1 (s2 (foldr step id [x3 .. xn])) z
             = ..
             = s1 (s2 (.. (sn (foldr step id [])) .. )) z
             = s1 (s2 (.. (sn id) .. )) z
             = (s2 (.. (sn id) .. )) (f z x1)
             = s2 (s3 (.. (sn id) .. )) (f z x1)
             = (s3 (.. (sn id) .. )) (f (f z x1) x2)
             = ..
             = sn id (f (.. (f (f z x1) x2) .. ) xn-1)
             = id (f (.. (f (f z x1) x2) .. ) xn)
             = f (.. (f (f z x1) x2) .. ) xn

in which xs = [x1 .. xn], si = step xi = \g z -> g (f z xi)

불명확 한 부분이 있으면 의견을 추가하십시오. :)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.