Extending your first program
Factor handbook ยป Your first program

Prev:Testing your first program


Our palindrome program works well, however we'd like to extend it to ignore spaces and non-alphabetical characters in the input.

For example, we'd like it to identify the following as a palindrome:
"A man, a plan, a canal: Panama."

However, right now, the simplistic algorithm we use says this is not a palindrome:
"A man, a plan, a canal: Panama." palindrome? .
f


We would like it to output t there. We can encode this requirement with a unit test that we add to palindrome-tests.factor:
{ t } [ "A man, a plan, a canal: Panama." palindrome? ] unit-test

If you now run unit tests, you will see a unit test failure:
"palindrome" test

The next step is to, of course, fix our code so that the unit test can pass.

We begin by writing a word which removes blanks and non-alphabetical characters from a string, and then converts the string to lower case. We call this word normalize. To figure out how to write this word, we begin with some interactive experimentation in the listener.

Start by pushing a character on the stack; notice that characters are really just integers:
CHAR: a

Now, use the Letter? word to test if it is an alphabetical character, upper or lower case:
Letter? .
t

Note: you might receive an error message that asks if you want to use the ASCII or Unicode support versions of the Letter? word. Choosing the Unicode version will allow Factor to continue running your code.

This gives the expected result.

Now try with a non-alphabetical character:
CHAR: #

Letter? .
f

What we want to do is given a string, remove all characters which do not match the Letter? predicate. Let's push a string on the stack:
"A man, a plan, a canal: Panama."

Now, place a quotation containing Letter? on the stack; quoting code places it on the stack instead of executing it immediately:
[ Letter? ]

Note: Quotations are similar to anonymous functions or blocks of code that have not been executed yet.

Finally, we pass the string and the quotation to the filter word, which will run your quotation and return a new string that contains only characters for which Letter? returns "true":
filter

The stack should now contain the following string: AmanaplanacanalPanama. This is almost what we want; we just need to convert the string to lower case now. This can be done by calling >lower; the > prefix is a naming convention for conversion operations, and should be read as "to":
>lower

Finally, let's print the top of the stack and discard it:
.

This will output amanaplanacanalpanama. This string is in the form that we want, and we evaluated the following code to get it into this form:
[ Letter? ] filter >lower

This code starts with a string on the stack, removes non-alphabetical characters, and converts the result to lower case, leaving a new string on the stack. We put this code in a new word, and add the new word to palindrome.factor:
: normalize ( string -- string' ) [ Letter? ] filter >lower ;

You will need to add unicode to the vocabulary search path, so that >lower and Letter? can be used in the source file.

We modify palindrome? to first apply normalize to its input:
: palindrome? ( string -- ? ) normalize dup reverse = ;

Factor compiles the file from the top down. So, be sure to place the definition for normalize above the definition for palindrome?.

Now if you press F2, the source file should reload without any errors. You can run unit tests again, and this time, they will all pass:
"palindrome" test

Congratulations, you have now completed Your first program!