You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: source_md/higher-order-functions.md
+24-22Lines changed: 24 additions & 22 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -673,18 +673,22 @@ It would be `map' f xs = foldl (\acc x -> acc ++ [f x]) [] xs`, but the thing is
673
673
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.
674
674
Sometimes you don't even have to do that.
675
675
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!**
677
677
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`:
679
685
680
686
```{.haskell:hs}
681
687
foldr f acc [] = acc
682
688
foldr f acc (x:xs) = f x (foldr f acc xs)
683
689
```
684
690
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:
688
692
689
693
```{.haskell:hs}
690
694
containsThree :: [Int] -> Bool
@@ -694,20 +698,20 @@ containsThree = foldr (\x rest -> if x == 3 then True else rest) False
694
698
Let's unfold this step-by-step on the infinite list `[1,2,3,4,...]`:
695
699
696
700
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**.
700
704
701
-
Why `foldl` Fails with Infinite Lists? Now, contrast this with `foldl`'s definition:
705
+
<br>Now, contrast this with `foldl`'s definition:
702
706
703
707
```{.haskell:hs}
704
708
foldl f acc [] = acc
705
709
foldl f acc (x:xs) = foldl f (f acc x) xs
706
710
```
707
711
708
712
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`:
711
715
712
716
```{.haskell:hs}
713
717
containsThreeFoldl :: [Int] -> Bool
@@ -716,20 +720,18 @@ containsThreeFoldl = foldl (\acc x -> if x == 3 then True else acc) False
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.
725
729
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).
727
731
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**.
729
733
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
733
735
734
736
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.
735
737
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