>

Example Project Solution

Suppose the problem statement were
Define a function called "remove" that delivers those elements of a sequence supplied as its second argument that are the same as a value supplied as its first argument.
This project asks you to define a function named "remove". The project description talks about two arguments for the function (a "second argument" and a "first argument). From this you can deduce that the definition of the function would start out with the function name ("remove") followed by two parameter names (one for each argument to be supplied to the function).

You decide what names you want to use for the parameters. The project description doesn't say much about the kind of data that might be supplied to the function - just that the second argument is a "sequence" and the first argument is a "value." It also says that any values in the sequence (which is the second argument) that are the same as the value (which is the first argument) are to be removed from the sequence. From this you can conclude that the first argument and the elements of the sequence supplied as the second argument are the same kind of data.

Since the description of the arguments has such a generic quality, your choices for the names of the parameters in the definition should reflect this lack of specificity. A choice of "x" as the name of the first argument and "xs" (pronounced "exes," which is supposed to suggest a bunch of x's) would have a suitably generic flavor.

With these decisions, you can conclude that the definition of the function will look something like this:

remove x xs = ... some formula goes here ...

But, what about the right hand side of the definition? What is "... some formula ..."? Well, the right hand side of a definition is a formula that specifies the value to be delivered by the function. In this case, that value is supposed to be pretty much the same as the second argument ("xs"), but with a few omissions.

That might suggest using a formula based on list comprehension (Lesson 5 in the Haskell text) because list comprehension specifies a new sequence whose elements come from a given sequence, but with some conditions (guards) on which of those elements are admissible.

I say "might suggest" more cavalierly than I should. Deciding what kind of formula to use to express the value delivered by the function is the only really creative step in this project. Mentioning that insight in an off-hand way is a common stylistic device of mathematicians - using phrases like "might suggest", as if it might just come upon you out of nowhere in your sleep, or when you're daydreaming.

To be fair to the mathematician, the insight may well have come during sleep, but only after months of deep thought while wide awake. I'm not suggesting that the insight required to solve problems in this course calls for months of deep thought. A few hours should do nicely for this project. Or, a few minutes if you've acquired a thorough understanding of the material of Lessons 1-6 - or if you get lucky. But, deciding what type of formula to use on the right-hand-side of the definition is the crucial step.

After deciding to use a list comprehension, you can write down a bit more of the definition:

remove x xs = [y | y <- xs, ... guard goes here ...]
That is, you can write this down as soon as you think of a name to use for a typical element from the sequence xs. I use the name "y" because (1) the value will be of the same kind as the elements of the sequence xs, (2) x is a value of that same kind, and (3) it is traditional in many high-school algebra problems to use the names "x" and "y" to denote values of the same kind.

Now, the guard is suppose to specify which elements are acceptable in the sequence denoted by the list comprehension. The project statement says that the elements acceptable in the sequence delivered by the function are those elements that are not the same as the value supplied by the first argument ("x"). So, with that observation, you can write down the missing guard.

remove x xs = [y | y <- xs, y /= x]

The definition of "remove" will be the only definition in your Haskell script for this project. Prepare a listing of it to turn in as one of the project deliverables. Use the Unix command

lpr -PprinterName scriptFilename
to produce the listing. In this case, since the script is contains only one definition, the listing will be short:
remove x xs = [y | y <- xs, y /= x]
That takes care of the first step in the project - defining the function.

The next step is to try the function out on some data chosen to illustrate the function's capabilities. Here are some cases to consider: (1) the function should deliver a sequence that not contain any elements with values equal to the first argument; (2) the function should not omit any other values; (3) the function should not insert any values not already there; (4) the function should deliver the values in the same order that they occurred in the sequence supplied as the second argument; (5) if the value of the first argument doesn't occur in the sequence supplied by the second argument, the function should deliver a sequence equal to the second argument; (6) if the second argument is the empty sequence, the value delivered should also be the empty sequence (special case of (5)).

Your Hugs session with your function to illustrate all of these properties, and your hand-written annotations should specify which properties each invocation of your function in the session is supposed to demonstrate. It might look like this:

Script started on Tue Sep 03 09:49:28 1996
... I removed some stuff here ... you shouldn't do this ...

Hugs session for:
/home/rlpage/fnl/hugs3/lib/Prelude.hs
remove.hs

handwritten annotation: example below removes the 't', leaves all else, including 'T', and in the same order

Main> remove 't' "Triathlon" 
"Triaholon"

handwritten annotation: next examples show that multiple elements are removed if they occur

Main> remove 'r' "rarified air"
"aified ai"
Main> remove ' ' "Madam I'm Adam, and you're not."
"MadamI'mAdam,andyou'renot."

handwritten annotation: next example illustrates the multiple-removal phenomenon, and shows that the function "remove" is polymorphic; that is, it can operate on different kinds of data

Main> remove "red" ["the", "red", "sun", "is", "a", "red", "ball"]
["the", "sun", "is", "a", "ball"]

handwritten annotation: But, the data must be consistent: "o" is a string, but the elements of "copy" are characters - can't even think about removing data that could never be there

Main> remove "o" "copy"

ERROR: Type error in application
*** expression     : remove "o" "copy"
*** term           : "o"
*** type           : String
*** does not match : Char

handwritten annotation: just showing off - can remove different elements by composing the function with itself

Main> (remove 'y' . remove 'o') "copy"
"cp"

handwritten annotation: things that aren't there won't be removed
Note: The element "little red wagon" is not the same as the value "red"

Main> remove "red" ["the", "little red wagon", "syndrome", "doesn't", "concern", "us"]
["the", "little red wagon", "syndrome", "doesn't", "concern", "us"]

Main> remove 'i' "feature"
"feature"

handwritten annotation: empty lists remain empty, no matter what kind of data they might contain

Main> remove 'o' ""
""

Main> remove "box" [ ]
[]

Main> :q
[Leaving Hugs]
> 
script done on Tue Sep 03 10:14:52 1996

Last Modified: