# Simply Scheme Chapter 14 – Common Patterns in Recursive Procedures

This chapter is about common programming patterns.

### The Every Pattern

In the `every` pattern, we collect the results of transforming each element of a word or sentence into something else.

One of the things the book talks about is a variation on the `letter-pairs` procedure. `letter-pairs` varies from the standard `every` pattern because it deals with two letters/words at a time:

The book then suggests tackling the following problem of having non-overlapping pairs (and contrasts the desired functionality with `letter-pairs`:

My first attempt to solve this problem could not handle even-numbered words correctly. This is because I only addressed a base-case where the number of letters remaining was equal to 1 and not where it was empty. If you’re going through a word two letters at a time, you’ve got to be able to handle base cases for both an odd number of letters and an even number of letters. The result of my inadequate procedure was that `disjoint-pairs` tried to take the `first` of an empty word when dealing with an input word with an even number of letters:

My second attempt failed to properly handle the case where one letter is passed initially, returning a word instead of a sentence (and also didn’t use `else`):

The working solution fixes the issue with the one-letter input by invoking `sentence` in the case where there is one letter remaining:

### The Keep Pattern

This time we’ll consider a different kind of problem: choosing some of the elements and forgetting about the others.

Here is an example of a `keep`-like pattern:

Let’s look at the differences between the `every` pattern and the `keep` pattern. First of all, the `keep` procedures have three possible outcomes, instead of just two as in most `every`-like procedures. In the `every` pattern, we only have to distinguish between the base case and the recursive case. In the `keep` pattern, there is still a base case, but there are two recursive cases; we have to decide whether or not to keep the first available element in the return value. When we do keep an element, we keep the element itself, not some function of the element.

Right. A typical `every` pattern is like this:

Since you’re doing the same thing to each element in a sentence until the sentence is empty, you don’t need to have logic that handles alternatives more complex than “is the sentence empty or not?” But with the `keep` pattern, you have to both handle the base case and also handle the alternatives of a given element either 1) being kept or 2) not being kept.

As with the `every` pattern, there are situations that follow the `keep` pattern only approximately. Suppose we want to look for doubled letters within a word:

Here is my solution to this. The `(<= (count wd) 1)` part lets it handle the empty and one word cases.

I noticed some differences between my solution and their solution (below):

One difference is that they don’t have `(word "")` in the first `cond` line, instead only using `""`. I think they are right here. `word` is unnecessary. `""` is the empty word, so it’s already a type of word. If you are returning it because you are e.g. calling `doubles` on a 1-letter word, then since it’s already a word, it doesn’t need to be made into a word by `word`. This is different than with `disjoint-pairs`, where we needed to invoke `sentence` in one of the base cases in order for the program to have the output we wanted. If you are arriving at the empty case after a series of recursive calls to `doubles`, then `doubles` will be passed as an argument to `word` as part of the recursive procedural calls anyways, so passing it to `word` again in the base case is redundant.

Another difference is that in the recursive call where a match is found, the argument to the recursive invocation of `doubles` is `(bf (bf wd))` instead of `(bf wd)` as in mine. Again, I think their approach makes sense. English doesn’t have “triples” of the same letter in a word. So, once you have found a match, you can be confident that the two letters that are “doubles” are dealt with for the purposes of the `doubles` procedure and move onto the next word. However, if you haven’t found a match, you have to continue going character by character (as in `(else (doubles (bf wd)))`), because you don’t know where the doubled characters will start within a word.

I think my approach is slightly superior in one respect: I handle the case of trying to pass an empty word to `doubles` whereas the book’s version gives an error message on it.

### The Accumulate Pattern

Another pattern is combining all the elements of an argument into a single result:

Note in both cases the use of the identity element in as the return value in the base case.

If there is no identity element for the combiner, as in the case of `max`, we modify the pattern slightly:

We can go all the way down to the empty sentence and then add 0 to a series of numbers or the empty word to a word and know the result will come out the same. This is because 0 and the empty word are the identity elements for the `+` and `word` procedures respectively. If there’s no identity element for the combiner, we want to cut off the recursion before it gets to the empty sentence. With `sent-max`, we call it quits when the `count sent` equals 1 and then return the final remaining value in the sentence.

### Helper Procedures

Found this section a bit tricky to follow so I’m going to go over it in detail.

Let’s say we want a procedure `every-nth` that takes a number n and a sentence as arguments and selects every nth word from the sentence.

We get in trouble if we try to write this in the obvious way, as a sort of `keep` pattern.

Here’s a `trace` of this version of the program on some sample input:

The problem is with the `**n**` that’s in boldface. We’re thinking that it’s going to be the `n` of the original invocation of `every-nth`, that is, 3. But in fact, we’ve already counted `n` down so that in this invocation its value is 1. (Check out the first half of the same `cond` clause.)

The way the program currently functions is this:

1) if `n` does not equal 1, the program recursively invokes `every-nth` with `(- n 1)` for the n value, up until the point where n does actually equal 1. This “countdown” is what results in the desired interval working one time – e.g. the first two words will be skipped for an n of 3, or the first 3 words will be skipped for an n of 4, and so on.
2) If `n` does equal 1, then the first value of the `sent` is returned, and `every-nth` is invoked with an `n` of 1. This basically means there is a “countdown” of 1 until the next value from the sentence is returned, leading to every word in the sentence being returned from then on.
3) If the sentence is empty, the empty sentence is returned.

This is not the desired behavior.

This procedure will correctly skip the first two words but will keep all the words after that point. That’s because we’re trying to remember two different numbers: the number we should always skip between kept words, and the number of words we still need to skip this time.

If we’re trying to remember two numbers, we need two names for them. The way to achieve this is to have our official `every-nth` procedure call a helper procedure that takes an extra argument and does the real work:

This version of `every-nth` takes the same input but provides `n` twice to a helper procedure, `every-nth-helper`, which receives the n-values as two arguments, `interval` (for the interval of words being selected) and `remaining` (to track the “countdown” to select he next word.)

When `remaining` is equal to 1, the `(first sent)` and is combined with a recursive call to `every-nth-helper`. The key thing here is that `interval` is provided not only as the first argument to `every-nth-helper` but as the second argument. The second argument is the the `remaining` that keeps track of the “countdown” to the next value to be selected from the sentence. By providing `interval` as the argument for `remaining` when selecting a sentence, `every-nth-helper` is “resetting the countdown”.

When `remaining` is not equal to 1, `every-nth-helper` just reduces `remaining` by 1.

This procedure always calls itself recursively with the same value of `interval`, but with a different value of `remaining` each time. `Remaining` keeps getting smaller by one in each recursive call until it equals 1. On that call, a word is kept for the return value, and we call `every-nth-helper` recursively with the value of `interval`, that is, the original value of `n`, as the new `remaining`. If you like, you can think of this combination of an initialization procedure and a helper procedure as another pattern for your collection.

### sent-before?

We want to write the procedure `sent-before?`, which takes two sentences as arguments and returns `#t` if the first comes alphabetically before the second. The general idea is to compare the sentences word by word. If the first words are different, then whichever is alphabetically earlier determines which sentence comes before the other. If the first words are equal, we go on to compare the second words.[5]

Their solution is as follows:

Here’s my explanation of this procedure:

The procedure doesn’t know whether the first or second sentence will have more words, so it needs to be able to handle both cases.

If the first sentence is `empty?`, that means that the first sentence ran out of words before the second sentence. That could happen if, for example, the first sentence and second sentence had the same 3 words to start, but then the second sentence had another word, e.g.:

In that case, the first sentence is alphabetically before the second sentence, and so the procedure should return true:

OTOH, if the second sentence runs out of words before the first, that means that the second sentence is alphabetically before the first sentence, so the procedure should return false.

The base case is the only part I thought needed a detailed explanation … the rest seems pretty straightforward to me.

### Exercises

Classify each of these problems as a pattern (`every`, `keep`, or `accumulate`), if possible, and then write the procedure recursively. In some cases we’ve given an example of invoking the procedure we want you to write, instead of describing it.

#### ✅ Exercise 14.1

(It’s okay if your solution removes the other `MORNING` instead, as long as it removes only one of them.)

This seems like a `keep` to me, though with a twist. We want to `keep` everything except one instance of the word to remove.

One way to approach this might be to do something like: after you hit the word, you skip over it but just immediately return the rest of the sentence (no recursive call, just a `bf sent`). Boom, problem solved.

Tests:

#### ✅❌ Exercise 14.2

I think this is closest to an `every`. We’re working our way through the constituent elements of a word and doing something with them. It’s a bit of a twist in that we’re not going letter by letter but instead going through groups of letters.

Correction: Programming solution is correct. Answer re: pattern is wrong. It’s more like `accumulate` cuz we’re building something up using the recursive call. We’re not transforming each element of something but making a new thing.

#### ✅ Exercise 14.3

(It’s okay if your procedure returns `(DI OB LA DA)` instead, as long as it removes all but one instance of each duplicated word.)

This is a `keep` pattern. We are keeping non-duplicates.

If the first word of the sentence is a member of the `butfirst` of the sentence, then `remdup` is recursively called with `bf sent` to skip the duplicate word. Otherwise, `sentence` is invoked with the first word of the sentence and a recursive call to `remdup (bf sent)` as arguments, which keeps the first word as part of the result.

#### ✅ Exercise 14.4

This is a `keep` pattern. We are keeping the odd-numbered words. We aren’t testing the

My test cases and output:

The `((= (count sent) 1) sent)` base case is necessary for handling sentences with odd-numbered numbers of words like `'(i lost my little girl)`. Because the recursive case `(else (se (first sent) (odds (bf (bf sent)))))))` moves through `sent` two at a time (with `(bf (bf sent))`), a sentence with an odd number of words will ultimately have one word remaining. If the recursive call is allowed to run again when the sentence has only 1 word remaining, the program will give an error because `bf` will be getting called twice on a sentence with only 1 word. Therefore, you need to handle this as a base case.

You also need to handle 0 words left in `sent` as a base case. This case will come up when the sentence has an even number of words. This is a separate base case because we want to do different things when we have 0 words left versus when we have one word left. When we have 1 word left, that means the word is the last word of a sentence with an odd-numbered number of words, and this being a procedure that returns the odd-numbered words within a sentence, we want to return such a word (which we do by returning `sent`). When we have 0 words left, that means the sentence had an even number of words, the last of which we may have “skipped past” in our last recursive call to the `odds` procedure, and so we just want to wrap up the procedure by returning the identity element for `se`, the empty sentence `'()`.

#### ✅ Exercise 14.5

Write a procedure `letter-count` that takes a sentence as its argument and returns the total number of letters in the sentence:

This is an `accumulate` pattern. We’re adding up the elements of a sentence into a single thing.

Here’s how i solved this in chapter 8:

Here’s how I solved it now:

I actually wrote a recursive helper procedure first but then I remembered that `count` exists 🙂

#### ✅ Exercise 14.6

Write `member?`.

We’re not taking each element of something and transforming it like with `every`, and we’re not choosing some elements and forgetting about others like with `keep`. We’re taking a word and a sentence and reducing them to a single boolean result. It doesn’t quite feel like an `accumulate` either though. I don’t think it fits within any pattern well.

I wrote `member2?` to avoid name conflicts with the existing `member?` procedure.

My tests (making sure that the output is the same as `member?`):

All outputs were as expected.

#### ✅ Exercise 14.7

Write `differences`, which takes a sentence of numbers as its argument and returns a sentence containing the differences between adjacent elements. (The length of the returned sentence is one less than that of the argument.)

This seems to be sort of like an `every`. We’re going through and transforming the elements. We’re not reducing them to 1 single value like in an `accumulate`. While there is a reduction in the number of values it’s only to (n – 1).

The base case is when `count numsent` is equal to 2 because we need two number values to figure out the difference of something. If we set the base case to less than two with this program as written, then we run into the common recursive procedure problem of a procedure trying to take the `first` of something that is empty.

This program works and generates the appropriate output. It could be further improved by rewriting as a `cond` in order to handle stuff like one or zero argument cases, but I’m going to skip doing that.

This method has the base case as the `count` of `numbers` being less than 2 and returns `'()` in that case. This is a bit better than mine, cuz it lets the program handle input sentences with fewer than 2 numbers.

#### ✅ Exercise 14.8

Write `expand`, which takes a sentence as its argument. It returns a sentence similar to the argument, except that if a number appears in the argument, then the return value contains that many copies of the following word:

I’m not sure this strictly falls within the patterns. We’re not `keep`-ing some elements and disregarding others, we’re not transforming `every` element, and we’re not accumulating things into a single value.

The structure of the `expand` procedure is as follows: first it checks if the sentence is empty and returns the empty sentence if so. Then it checks if the first word in the sentence is a number. If so, it passes both that number and the word that follows it to a helper procedure `expand-helper`. An invocation of `sentence` joins the result of `expand-helper` and a recursive call to `expand`. The recursive call to `expand` is made with a `sent` that has been reduced by two words due to the use of two `butfirsts`. If the sentence is not empty and the first word of it is not a number, then `sentence` is invoked with the first word of sent and a recursive invocation of `expand` used as arguments. This recursive invocation of expand only moves ahead one word in the sentence.

#### ✅ Exercise 14.9

Write a procedure called `location` that takes two arguments, a word and a sentence. It should return a number indicating where in the sentence that word can be found. If the word isn’t in the sentence, return `#f`. If the word appears more than once, return the location of the first appearance.

This is a procedure where we’re going through and searching for whether one value matches another and then returning the location. I don’t think this fits neatly into any of the patterns. ADDENDUM: AnneB argues this is a keep pattern “except we are not keeping an element of the input sentence, but instead keeping the location of that element. We are also stopping once we find one position to keep, and not looking at the rest of the sentence.” I don’t strongly disagree but I’m not sure I’m convinced either. Those two differences sound pretty major to me.

#### ✅ Exercise 14.10

Write the procedure `count-adjacent-duplicates` that takes a sentence as an argument and returns the number of words in the sentence that are immediately followed by the same word:

This seems sort like a mix of `keep` and `accumulate`. Not all the words are used — only the words that meet certain criteria get counted (kinda like `keep`). Words followed by a duplicate return a 1, otherwise they return a 0. The 1’s get added up (getting reduced to a single value, kinda like with `accumulate`).

The base case tests for one word remaining to avoid the issue of calling `butfirst` on an empty sentence. At the point there is only one word left in the sentence, we know there will be no more duplicates.

Each pair of values within the sentence is tested for being a duplicate with the helper procedure `is-dupe`. If they are a duplicate, this helper returns 1, and if not, it returns 0. The values are summed by `+`.

I should have named `is-dupe` something like `check-if-dupe`.

#### ✅ Exercise 14.11

Write the procedure `remove-adjacent-duplicates` that takes a sentence as argument and returns the same sentence but with any word that’s immediately followed by the same word removed:

Conceptually this is a `keep` procedure in that we’re going through some elements and only keeping those that meet a certain criteria (i.e. not being a duplicate).

I followed the style of my previous answer but with some modifications.

If the `count` of the sentence gets down to 1, we want to return `sent`, since that’s the last word of the sentence. Our instruction is to remove any word that is immediately followed by the same word. The last word of the sentence will, by definition, never be followed by the same word, or any other word for that matter.

If the `count` isn’t 1, we call our modified helper procedure with the first two words in the sentence. The helper procedure returns the empty sentence if the words match, which effectively removes the first word from the sentence. If the words don’t match, it returns `wd1`, which keeps the first word within the sentence. We don’t need to really worry about `wd2` right now except insofar as we are using it for comparison. (If, in the next recursive procedure call, there are still at least two words left, then our current `wd2` will be `wd1` then.) Using `sentence`, we combine the results of `is-dupe` and a recursive call to `remove-adjacent-duplicates`.

Again, I should have named `is-dupe` something like `check-if-dupe`.

ADDENDUM: Initially I had the base case of `remove-adjacent-duplicates` as `(if (= (count sent) 1) sent`, but I ultimately rewrote it as you see above. The rewrite handles an empty sentence as input.

#### ✅ Exercise 14.12

Write a procedure `progressive-squares?` that takes a sentence of numbers as its argument. It should return `#t` if each number (other than the first) is the square of the number before it:

I don’t think this fits neatly into `keep`, `every`, or `accumulate`.

ADDENDUM: AnneB argues that this is an `accumulate` pattern because the procedure accumulates using `and`. I disagree because the elements of the input sentence themselves are not being combined into a single result. Instead, if they pass the test of being a square, they get changed into a truth value, and those truth values are arguably accumulated. But transforming + accumulating is at least a somewhat different kind of thing than just accumulating.

This is pretty similar to some of my last few solutions. Here I’m using `and` as the thing combining values, and I’m dealing with true/false values instead of words or numbers.

ADDENDUM: Initially I had the base case of `remove-adjacent-duplicates` as `(if (<= (count sent) 1) #t`, but ultimately rewrote it as you see above. The rewrite handles an empty sentence as input.

#### ✅ Exercise 14.13

What does the `pigl` procedure from Chapter 11 do if you invoke it with a word like “frzzmlpt” that has no vowels? Fix it so that it returns “frzzmlptay.”

They use `pigl` as an `every` example in the text.

Here is the original `pigl` procedure:

with a vowel-less word, the `pigl` procedure recursively calls itself endlessly, trying to find a vowel.

I incorporated a `vowel?` procedure. My solution seems maybe a bit inelegant but it does the job:

ADDENDUM: I realized I only needed one `if` statement:

ADDENDUM: I thought he difference in the way AnneB and I handled this was interesting. I basically made a special check for the no-vowel case using a helper procedure, while AnneB made a helper that did a countdown based on the length of the word to ensure that each letter was only checked once.

ADDENDUM: I had some intuition that my helper procedure was inelegant. I found a better one from pongsh

Instead of checking the input word for the presence of each vowel, it checks each letter of the word against a word with all the vowels.

#### ✅ Exercise 14.14

Write a predicate `same-shape?` that takes two sentences as arguments. It should return `#t` if two conditions are met: The two sentences must have the same number of words, and each word of the first sentence must have the same number of letters as the word in the corresponding position in the second sentence.

non-standard pattern. AnneB says it’s an `accumulate`. Same disagreement as in 14.12 so see that for my explanation of my current view.

Explanation:

If both sentences are empty, return true, because this will mean that the sentences were the same “shape”.

If either sentence is empty (but not both, since we already tested for that) then return false, since that means that one sentence ran out of words before the other sentence, failing one of our conditions.

If it is not the case that the first word of `sent1` and `sent2` have the same number of characters, return false. This ensures that the number of letters of each word in the respective sentences are the same when the program returns true.

We arrive at the `else` only if neither sentence is empty and if the first words of the respective sentences are the same length. In that case, we recursively call `same-shape?` with the `butfirsts` of both sentences.

This procedure is a bit inefficient cuz it tests the length of the sentences each time when you really only need to do that once. If you cared about efficiency, you could do the length test once and do the rest of the stuff in a helper. I’m not worried about the performance of this program on my Mac though 🙂

#### ✅ Exercise 14.15

Write `merge`, a procedure that takes two sentences of numbers as arguments. Each sentence must consist of numbers in increasing order. `Merge` should return a single sentence containing all of the numbers, in order. (We’ll use this in the next chapter as part of a sorting algorithm.)

Initially was not sure re: pattern. It’s not straightforward. I think `every` is the closest.

It’s definitely not a `keep` because we’re not discarding anything, so that one is is easy.

It’s not a straightforward `accumulate` cuz we’re not reducing the elements of an argument to a single result. We are, however, taking two sentence arguments and reducing them to a single sentence.

Maybe `every` is the closest. We’re not collecting the results of transforming each element of a sentence into something else. We are, however, collecting the results of reordering the elements of two sentences.

Without `((empty? sent2) sent1)`, this happens with their example input:

That’s because the second sentence runs out numbers first, since the first sentence is the one with the highest number. If you give the second sentence the highest number, then you need `((empty? sent1) sent2)` or else the procedure breaks in the same way.

Since this sort of procedure wouldn’t normally wind up with both sentences empty, I think `((and (empty? sent1) (empty? sent2)) '())` only handles the case where you provide two empty sentences as arguments.

`smaller-num-first?` tests whether the first number from the first sentence is smaller than the first number of the second sentence. If that’s true, I invoke `sentence` with the first number of the first sentence as the first argument, since it is the smaller number and we want these sorted in order. I then recursively call `merge` with `(bf sent1)` and `sent2`. We only want to `bf` the first sentence since it’s only a number from the first sentence that we’ve sorted so far.

If none of these other conditions are met, I assume the second number is the smaller of the two numbers and recursively call `merge` accordingly.

ADDENDUM: Not sure why I broke out the test for whether the smaller number was first was in a helper procedure, as it is quite easy to check directly as AnneB does:

#### ✅ Exercise 14.16

Write a procedure `syllables` that takes a word as its argument and returns the number of syllables in the word, counted according to the following rule: the number of syllables is the number of vowels, except that a group of consecutive vowels counts as one. For example, in the word “soaring,” the group “oa” represents one syllable and the vowel “i” represents a second one.
Be sure to choose test cases that expose likely failures of your procedure. For example, what if the word ends with a vowel? What if it ends with two vowels in a row? What if it has more than two consecutive vowels?
(Of course this rule isn’t good enough. It doesn’t deal with things like silent “e”s that don’t create a syllable (“like”), consecutive vowels that don’t form a diphthong (“cooperate”), letters like “y” that are vowels only sometimes, etc. If you get bored, see whether you can teach the program to recognize some of these special cases.)

non-standard pattern.

##### First Attempt

Here’s my first attempt at an answer for the basic part of this question. It only handles vowel groupings that are two letters long and does not address any of the special cases:

I think it’s relatively straightforward with one exception. In the line that handles two vowels next to each other, `((and (> (count wd) 1)(vowel? (first wd))(vowel? (first (bf wd)))) (+ 1 (syllables (bf (bf wd)))))`, I have `(> (count wd) 1)`. That’s there so that the rest of that line does not run in the situation where there is a vowel and only one letter remaining, like when a word ends in a vowel – as with “bake” – or consists of a single vowel.

Had some trouble figuring out an improved solution that would handle multiple vowels next to each other.

##### Second Attempt

This procedure does the job but i’m afraid it’s “cheating” a bit because it’s using the higher order procedure repeated and i think maybe we’re not supposed to use those for recursive exercises.

The basic idea here is that I’m using `syllables` to find out when a vowel starts and then using `vowel-segment` to figure out the actual content of the grouping of vowels. I can then use `count` to figure out how big the group of vowels is and `repeated bf [number of vowels]` to skip over however many vowels in a row there are.

##### Third Attempt

I coded around the “higher order procedure” issue by just making a special case recursive `bf-repeater` procedure, lol:

Wasn’t happy with last approach, so I tried another approach: looking for boundaries between vowel and consonant. This approach has the advantage of only needing to consider two letters at a time – rather than needing to keep track of a whole group of vowels†, you only need to check whether two letters represent a boundary between vowels and consonants. And if a vowel ends the sentence you can treat that as a special case.

†You typically don’t need to worry about three letter vowel groups in English but the authors raised the issue as part of the exercise so I wanted to make sure my procedure could handle it.

After some iterating I came up with this:

Some explanation:

`vowel-consonant-boundary` looks for when a vowel follows a consonant and returns 1 if that’s the case and 0 if not. So with a word like apple, the letter group `ap` gets counted as one syllable, as does the letter group `le`. Or with `soaring`, the `ar` gets counting as a syllable, as does the `in`. It’s the transition points between vowel and consonant that seem to demarcate a syllable, so those seem like the logical thing to count.

`((and (= (count wd) 1)(vowel? wd)) 1) ` returns 1 if the last letter is a vowel. This handles cases where there are single letter or short words ending in a vowel. For example, “a” is a word with no vowels that precede consonants cuz it only has 1 letter. “boo” likewise has no vowels preceding consonants. `((and (= (count wd) 1)(vowel? wd)) 1)` ensures that these words get at least one syllable.

For the special cases like silent-e, I wasn’t aware of some overall pattern in the word that I could search for, so I conceptualized the solution as basically having a list of words that have a silent-e. I only picked a few examples – I’m sure I could have found a huge list and pasted them in, but I didn’t see the point in that. My goal was to figure out how to solve the overall programming problem assuming that I’d use a word list for handling special cases, not fully address the special cases. So the word lists I used are short and only illustrative.

For silent-e words we want 1 syllable less than default behavior (“like” would give two syllables under our standard rule but we only want 1).

For words like “continuum” that have adjacent vowels without a dipthong (meaning “u” and “um” are two different sounds – you don’t say con-tin-um, you say con-tin-you-um) we want one more syllable than the default behavior.

The book used the example of “cooperate” for no-dipthong, but that’s a tricky one to bring up cuz it actually has both a no-dipthong adjacent vowel thing (co-op) and a “silent e” at the end (you don’t say co-op-er-ayt-ee, you say co-op-er-ayt – the e has some affect in terms of what sound the “a” makes but it’s not its own sound). But with this organization of the program “cooperate” can be in both lists and get counted accordingly – the +1 and -1 offset.

tests:

##### Other Solutions

I’m going to try analyzing other solutions to this problem like AnneB did. I think it’s a good idea.

###### buntine

`repeats` represents consecutive vowels.
`n` represents the total syllable count.

Set `increment` to 1 if `repeats` is greater than 0, or to 0 otherwise. (in other words, if there was a preceding vowel, set `increment` to 1)

If the word is empty, add `increment` and `n`. This is the value that will return at the end of the procedure.

If the first letter is a vowel, recursively call `count-syllables` with `bf` of `wd`, the same `n`, and `(+ repeats 1)` for `repeats`. This will have the result of setting `increment` to 1 in the recursive call.

Otherwise (meaning, if the first letter of `wd` is a consonant), recursively call `count-syllables` with `bf wd`, add `increment` and `word` and pass that in as `n`, and pass in 0 for `repeat`.

So how does this program function in the big picture?

Every time this procedure hits a vowel, the next recursive call to the function will have `increment` set to 1. The next time the program hits a non-vowel, or the end of the program, the `increment` and `n` values get summed, and this is the value that gets returned when the program finishes (when the word is empty). So what the program is basically tracking as a syllable is the sum of the number of transitions from vowels to consonants, with an additional syllable added on if the word ended in a vowel or vowels.