Skip to content

Commit 58b7b23

Browse files
Xingye-Dujingulysses4ever
authored andcommitted
Divide into two subsections
1 parent 46cb1af commit 58b7b23

1 file changed

Lines changed: 24 additions & 22 deletions

File tree

source_md/higher-order-functions.md

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -673,18 +673,22 @@ It would be `map' f xs = foldl (\acc x -> acc ++ [f x]) [] xs`, but the thing is
673673
If you reverse a list, you can do a right fold on it just like you would have done a left fold and vice versa.
674674
Sometimes you don't even have to do that.
675675
The `sum` function can be implemented pretty much the same with a left and right fold.
676-
*One big difference is that right folds work on infinite lists, whereas left ones don't!*
676+
**One big difference is that right folds work on infinite lists, whereas left ones don't!**
677677

678-
How `foldr` works with infinite lists? Recall the definition of `foldr`:
678+
**Folds can be used to implement any function where you traverse a list once, element by element, and then return something based on that.
679+
Whenever you want to traverse a list to return something, chances are you want a fold.**
680+
That's why folds are, along with maps and filters, one of the most useful types of functions in functional programming.
681+
682+
### Why `foldr` works on infinite lists while `foldl` doesn't
683+
684+
Recall the definition of `foldr`:
679685

680686
```{.haskell:hs}
681687
foldr f acc [] = acc
682688
foldr f acc (x:xs) = f x (foldr f acc xs)
683689
```
684690

685-
The key insight is that the recursive call `foldr f acc xs` is passed as an argument to the function `f`. Because Haskell is lazy, if `f` never uses its second argument, the recursive call is never evaluated. This allows `foldr` to terminate on infinite lists, provided `f` is lazy in its right argument.
686-
687-
Example: Checking for a Value in an infinite list. Suppose we want to check if `3` appears in an infinite list `[1..]`. We can use `foldr` like this:
691+
The key insight is that the recursive call `foldr f acc xs` is passed as an argument to the function `f`. Because Haskell is lazy, if `f` never uses its second argument, the recursive call is never evaluated. This allows `foldr` to terminate on infinite lists, provided `f` is lazy in its right argument. For example, we want to check if `3` appears in an infinite list `[1..]`. We can use `foldr` like this:
688692

689693
```{.haskell:hs}
690694
containsThree :: [Int] -> Bool
@@ -694,20 +698,20 @@ containsThree = foldr (\x rest -> if x == 3 then True else rest) False
694698
Let's unfold this step-by-step on the infinite list `[1,2,3,4,...]`:
695699

696700
1. Start: `foldr (...) False [1,2,3,4,...]`
697-
2. For `x=1`: `if 1 == 3 then True else (foldr (...) False [2,3,4,...])` -> `1 /= 3`, so this becomes `foldr (...) False [2,3,4,...]` (we recurse).
698-
3. For `x=2`: `if 2 == 3 then True else (foldr (...) False [3,4,5,...])` -> `2 /= 3`, so this becomes `foldr (...) False [3,4,5,...]` (we recurse again).
699-
4. For `x=3`: `if 3 == 3 then True else (foldr (...) False [4,5,6,...])` -> `3 == 3` is `True`! *Here, `f` ignores the recursive call (the [4,5,6,...] part) and returns True immediately*.
701+
2. For `x = 1`: `if 1 == 3 then True else (foldr (...) False [2,3,4,...])` $\to$ `1 /= 3`, so this becomes `foldr (...) False [2,3,4,...]` (we recurse).
702+
3. For `x = 2`: `if 2 == 3 then True else (foldr (...) False [3,4,5,...])` $\to$ `2 /= 3`, so this becomes `foldr (...) False [3,4,5,...]` (we recurse again).
703+
4. For `x = 3`: `if 3 == 3 then True else (foldr (...) False [4,5,6,...])` $\to$ `3 == 3` is `True`! **Here, `f` ignores the recursive call (the `[4,5,6,...]` part) and returns `True` immediately**.
700704

701-
Why `foldl` Fails with Infinite Lists? Now, contrast this with `foldl`'s definition:
705+
<br>Now, contrast this with `foldl`'s definition:
702706

703707
```{.haskell:hs}
704708
foldl f acc [] = acc
705709
foldl f acc (x:xs) = foldl f (f acc x) xs
706710
```
707711

708712
Notice that `foldl` always makes a recursive call first, passing `f acc x` as the new accumulator. This means:
709-
Even if the accumulator becomes True at some point, foldl will still continue recursing down the list.
710-
For an infinite list, this leads to infinite recursion (it never stops). If we try to implement containsThree with `foldl`:
713+
Even if the accumulator becomes `True` at some point, `foldl` will still continue recursing down the list.
714+
For an infinite list, this leads to infinite recursion (it never stops). If we try to implement `containsThree` with `foldl`:
711715

712716
```{.haskell:hs}
713717
containsThreeFoldl :: [Int] -> Bool
@@ -716,20 +720,18 @@ containsThreeFoldl = foldl (\acc x -> if x == 3 then True else acc) False
716720

717721
Unfolding on `[1,2,3,4,...]`:
718722

719-
1. Start: `acc = False`, list [1,2,3,4,...].
720-
3. Compute `f False 1` -> `if 1==3 then True else False` -> `False`. Recurse: `foldl (...) False [2,3,4,...]`.
721-
4. Compute `f False 2` -> `False`. Recurse: `foldl (...) False [3,4,5,...]`.
722-
5. Compute `f False 3` -> `True`. But now we must recurse: `foldl (...) True [4,5,6,...]`.
723-
6. Compute `f True 4` -> `True` (since 4 /= 3, it returns the accumulator True). But we recurse again: `foldl (...) True [5,6,7,...]`.
724-
7. This continues forever. Even though we found 3, we never stop.
723+
1. Start: `acc = False`, list `[1,2,3,4,...]`.
724+
3. Compute `f False 1` $\to$ `if 1==3 then True else False` $\to$ `False`. Recurse: `foldl (...) False [2,3,4,...]`.
725+
4. Compute `f False 2` $\to$ `False`. Recurse: `foldl (...) False [3,4,5,...]`.
726+
5. Compute `f False 3` $\to$ `True`. But now we must recurse: `foldl (...) True [4,5,6,...]`.
727+
6. Compute `f True 4` $\to$ `True` (since `4 /= 3`, it returns the accumulator `True`). But we recurse again: `foldl (...) True [5,6,7,...]`.
728+
7. This continues forever. Even though we found `3`, we never stop.
725729

726-
The `foldr` function can terminate on infinite lists if the function `f` is lazy in its right argument (i.e., `f` can short-circuit by ignoring the recursive result).
730+
<br>The `foldr` function can terminate on infinite lists if the function `f` is lazy in its right argument (i.e., `f` can short-circuit by ignoring the recursive result).
727731
The `foldl` function always processes the list from left to right and cannot short-circuit in the same way, so it fails on infinite lists.
728-
This is why `foldr` is often more suitable for working with infinite lists. Just remember: *the function `f` must be lazy in its right argument for `foldr` to terminate early*.
732+
This is why `foldr` is often more suitable for working with infinite lists. Just remember: **the function `f` must be lazy in its right argument for `foldr` to terminate early**.
729733

730-
**Folds can be used to implement any function where you traverse a list once, element by element, and then return something based on that.
731-
Whenever you want to traverse a list to return something, chances are you want a fold.**
732-
That's why folds are, along with maps and filters, one of the most useful types of functions in functional programming.
734+
### Other folds
733735

734736
The `foldl1`{.label .function} and `foldr1`{.label .function} functions work much like `foldl` and `foldr`, only you don't need to provide them with an explicit starting value.
735737
They assume the first (or last) element of the list to be the starting value and then start the fold with the element next to it.

0 commit comments

Comments
 (0)