Skip to content

Commit c1c6a1e

Browse files
committed
add explanation of use of foldr
1 parent e78bef5 commit c1c6a1e

1 file changed

Lines changed: 32 additions & 0 deletions

File tree

source_md/modules.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,38 @@ search needle haystack = foldr step False (tails haystack)
348348
First we call `tails` with the list in which we're searching.
349349
Then we go over each tail and see if it starts with what we're looking for.
350350

351+
Maybe you're surprised to see `foldr` being used here, instead of `foldl'`.
352+
Why aren't we going through the list simply from left to right, like a normal person?
353+
Ah, but we are!
354+
You see, `foldr` does not *really* go through lists from right to left.
355+
It actually walks the list from left to right, just like `foldl'`.
356+
The real difference is not the direction, but the way the end result is composed.
357+
358+
In a strict left fold, at every step the accumulator is immediately updated.
359+
Nice, no time or space wasted.
360+
The catch is that `foldl'` refuses to stop until it has reached the end of the list.
361+
The accumulation function you pass it has no say in the matter.
362+
This is unfortunate: if we have already found what we're looking for, there is little point in continuing the search!
363+
364+
This is where the lazy right fold comes in.
365+
`foldr` actually gives its accumulation function some control, which allows us to stop searching early.
366+
At each step `foldr` is like: hey combining function, here's this list element that I found.
367+
Do with it what you will.
368+
I'd rather not go further down the list, because I'm lazy, but if you really need me to I will and I'll pass the result of that to you as a second argument.
369+
If the combining function at any point does not really need its second argument, then the list walking can stop.
370+
But if it does need its second argument then, well, the walk must go on.
371+
372+
Coming back to the implementation of `search`, we see an element--accumulator combining function appropriately named `step`.
373+
From `foldr` it receives elements of `tails haystack`, which are themselves lists and therefore called `segment` in this code.
374+
For each `segment` we check whether it matches the `needle`.
375+
If it does, then we're done.
376+
No need to even look at the second argument, we can just return `True` immediately and thereby stop the search.
377+
But if it does not match the `needle`, then we need to continue.
378+
Fortunately, `foldr` also passes `step` a second argument: the promise to continue the search, should it need it to.
379+
I have cheekily chosen to call this promise `continue`.
380+
Not yet having found our `needle`, we have no choice but to take `foldr` up on its promise.
381+
We therefore return `continue`.
382+
351383
With that, we actually just made a function that behaves like `isInfixOf`{.label .function}.
352384
`isInfixOf` searches for a sublist within a list and returns `True` if the sublist we're looking for is somewhere inside the target list.
353385

0 commit comments

Comments
 (0)