Harvey Mudd College
Computer Science 60
Assignment 1, Due Sunday, January 25, by 11:59 pm


Link to the CS 60 submission site
Back to the top-level assignments page
Back to the top-level CS 60 page

Rex: the king of computation!

This homework is a chance to get familiar with two things:

  1. A command-line environment -- in particular on the CS department's main compute server, named knuth.
  2. A functional programming language named Rex that has a number of features that make it different than Python, CS 5's programming language.

The Problems

Problem 0:    Getting familiar with unix and Rex

[0 points ~ getting started]

You are welcome to obtain any and all help on this problem!

Why bother with the command line?

You may not have navigated/edited files and folders via the command-line before, or perhaps only a bit. Why, given today's dominance of the WIMP interface (Windows, Icons, Mouse, and Pointer), would one spend time at a text-based command line, sometimes called the shell?

There are at least two reasons. The first is that a great deal of computation outside of CS depends on a command-line interface. For instance, all of the interaction done in matlab occurs through a command line -- it just happens to be a command line wrapped in a matlab window. The same is true of mathematica, maple, and many statistical packages, as well. Transfering files from machine to machine and logging into machines remotely, especially if they are running different operating systems, are efficiently done via command line.

In a remarkable bout of procrastination, we took a survey of seven non-CS Profs in Olin (Profs. Levy, Adolph, Su, dePillis, Benjamin, Bush, and McFadden): everyone used command-line skills in their work; three did so extensively or almost exclusively. We look forward to extending our survey into Parsons and Jacobs -- perhaps when some unenjoyable deadlines are looming -- the results there are likely to be the same. So, the first reason for introducing command-line computation is that feeling comfortable at the shell/command line is a "resume-listable" skill that will make you far more effective with computation as used in all technical disciplines today.

The second reason, and certainly the one more in keeping with the philosophy of CS 60, is that basic familiarity with the command line provides an accurate, flexible, and powerful mental model of how computation is organized on today's machines. A graphical interface is wonderful for undertaking the tasks it was designed to handle. The command line, on the other hand, opens up all of the computational facilities of a particular machine. The command line provides a view into what is going on, quite literally, "behind the curtain," of modern interfaces.

Try it!

This problem includes instructions for using the command line to get around the main CS server named knuth (full name: knuth.cs.hmc.edu). Here are the steps:

Feel free to skip any of these steps that you are already familiar with... . Even if you're completely comfortable with the command line and remotely accessing, editing, and running files, you might want to skim the Rex-specific parts.

References

The first few links on the CS 60 references page provide much more detail on the unix operating system, using the command line, and a Rex reference.



Design, commenting, and formatting

Design and formatting account for roughly a quarter of the credit for each function or program you complete.

Design, here, means algorithm design, that is, how you break a problem down into smaller pieces and then compose those pieces into an overall solution. Large, convoluted functions are difficult to understand (and debug!). Use small, clear functions -- with helper functions of your own design, as needed or desired.

Formatting includes coding style: clean and clear structure, meaningful variable and function names, ample use of spacing, and indenting that reflects code structure. One-letter variable and other short variable names are OK for commonly-used idioms and small functions, such as [f|R] representing the first and rest of a list or fac(x), specifying the factorial of a number x. However, longer names that describe a variable more fully are an excellent form of "self-documenting" programs that are easy to understand!

As for commenting, in each assignment we ask you to include

  • a top-of-file comment with
    • your name
    • your partner's name, if any
    • how long you spent on it (roughly)
    • any other comments
    Those additional comments are optional, but appreciated -- they are a good way to let the graders and us know about particular difficulties or thoughts about a problem. We encourage Any and all feedback/suggestions!
  • a comment for each function describing
    • the function's input(s) and output(s)
    • briefly, in English, what it does.
    These help, in particular, in creating recursive functions. The example part1.rex file has a template you are welcome to follow for this.
  • Comments within functions or data definitions are welcome, but not required unless something tricky is going on.
Below is an example -- this file is also on knuth at /cs/cs60/hwfiles/a1/part1.rex . Feel free to copy this template and use it!

part1.rex, with comment and testing templates

Important warning about function names!    Be sure to name your functions as indicated in the problem statements below -- including capitalization and punctuation! This helps our graders, who can automate the running of many tests using those function names. Helper functions may have any name you like, but they should be descriptive and appropriate. When graders have to manually change your function names to match the ones in the homework problem descriptions, it angers them -- and it's always a good rule of thumb to avoid angering the graders!

Testing your code

Computation in the form of software is useful to the extent that it works. But it only works to the extent that it has been tested! Testing your own software thoroughly is important -- we use test cases to check how well your functions work, and you will certainly want to do the same... .

Some test cases are indicated for each of the problems. These can be copied from this assignment site into your code. When you use auxiliary functions, it is good practice (and will save time in the long run) to include tests for them, too. We strongly recommend creating additional tests for the primary functions, because the tests provided do omit some of the cases you'll need to handle!


Built-in functions

You're welcome to use any built-in functions for this week's assignment except remove_duplicates and pow, since we are implementing those two functions. In particular, some of these may be particularly useful:

append(L,M)     // concatenates two lists (inputs must be lists!)               
member(e,L)     // 0 if e is not in L; 1 if e is in L
reverse(L)      // reverses L
rmv1(e,L)       // not built-in; from class -- removes one e from L
length(L)       // returns the length of L         
max(x,y)        // returns the max of x and y 
min(x,y)        // returns the min of x and y
sort(L)         // returns a sorted version of L


part1.rex    Individual problems, #1 through #5

Submit definitions for these functions in a file named part1.rex. Each person should write these first five problems on their own. We encourage you to seek out help from us, from the grutors, or from other students as described in the syllabus.

Problem 1:    pwr

[10 points, individual]

For this problem define a function, pwr( b, p ) that outputs the value of b raised to the p power; b will be any numeric value, and p will be an integer. With all of your functions in rex, you do not have to check that the inputs are valid; we will test only with valid inputs.

Rex does have this function built-in; it's named pow - but using pow isn't allowed here! Instead, use recursion. You may want to build from the fac example already in part1.rex and that we tried in class.

Your pwr function should handle negative powers correctly. Warning: as in many languages, when you want floating-point division, be sure to make the numerator 1.0! If both the numerator and denominator are integers, it will make the result an integer by rounding down -- for this problem, you won't want that behavior.

In addition, it should return 1.0 whenever p is zero, and it should return 0.0 whenever b is zero, except when p is also zero. (It can do anything for zero-to-the-zeroth power; we won't test that case.)

As an example of a suitable pre-function comment, feel free to use these lines:

//  pr: #1
// fun: pwr(b,p)
//  in: b - the numeric base, p - an integer power
// out: b**p (floating point!)

 ... your rules here ...

Here are a couple of tests to try -- feel free to copy-and-paste these under your definition of pwr.

test( pwr(3,5),   243 );
test( pwr(2,-2),  0.25 );


Problem 2:    lg2

[10 points, individual]

Mathematicians define logarithms in lots of ways: as an exponent, as the inverse of a power function, as sums of certain series, ... .

To compuer scientists, by far the most common definition of lg2(x) is this: it's the number of times you can repeatedly divide x by 2 until you're ≤ 1.. For example, lg2(8) returns 3, because dividing by two repeatedly yields 8 -> 4 -> 2 -> 1 for a total of three divisions.

Using this idea - and recursion - write the function lg2(x), which takes in x, a floating-point value greater than or equal to 1. It should output an integer which is the number of times that x can be divided by two before the result is ≤ 1. Thus, for x ≤ 1, the function lg2(x) should output zero.

Another floating-point warning: I found it easier to write this function using x/2.0 rather than x/2 within the recursive step. Remember that the former is floating-point division; the latter is integer-only division and always yields integer output when x is an integer

Here are a few tests to check:

test( lg2(7),  3 );
test( lg2(8),  3 );
test( lg2(9),  4 );
Officially, this is the ceiling (rounding up) of the log-base-2 of x, and it's probably no surprise that base-2 is by far the most common logarithm used in CS.


Problem 3:    twiddle

[10 points, individual]

Write a function twiddle( L ) taking as input any list L. Then, twiddle( L ) should return a list identical to L, but with the first two elements reversed. If L has fewer than two elements, then twiddle should output a list identical to its input.

Here are some example tests:

test( twiddle( [1,2,3,4] ),    [2,1,3,4] );
test( twiddle( [1,2] ),        [2,1] );
test( twiddle( [ [1,2] ] ),    [[1,2]] );  // L has only 1 element!


Problem 4:    superreverse

[10 points, individual]

This problem asks you to write a different variant of reverse. You are welcome and encouraged to use the built-in reverse function to help with superreverse( L ) here and duperreverse( L ) in the next problem. Both superreverse and duperreverse take any list L as input.

Your function superreverse( L ) should return a list like L except that all top-level lists that are elements of Lshould be reversed.. Here are two examples:

test( superreverse( [ [1,2], 42, "string", [5,4,3] ] ),
      [ [2,1], 42, "string", [3,4,5] ] );

test( superreverse( [ [1,2], [3,4,[5,6],7], 8, [[9]] ] ),
      [ [2,1], [7,[5,6],4,3], 8, [[9]] ] );
How to know if a top-level element is a list or not? Rex has a built-in function named is_list( x ) that returns 1 (true) if its input x is a list and 0 (false) if its input x is not a list. You'll want to use is_list here.


Problem 5:    duperreverse

[10 points, individual]

The function duperreverse( L ) should return a list like L, but with all list structure - top-level and nested - reversed! Again, use is_list. Here are some examples:

test( duperreverse( [ [1,2], 42, "string", [5,4,3] ] ),
      [ [3,4,5], "string", 42, [2,1] ] );

test( duperreverse( [ [1,2], [3,4,[5,6],7], 8, [[9]] ] ),
      [ [[9]], 8, [7,[6,5],4,3], [2,1] ] );

Hints and warnings: it is important to use the functions list (or []), cons (or [|]), and append(L,M) in a composition that preserves the nesting-depth of all of the elements of L. Note that you can't use cons to put things at the end -- for that, use append! But note that append(L,M) always takes two lists as input. You'll want to be sure that top-level elements stay as top-level elements, and that sublists stay as sublists.



part2.rex    Pair or individual problems, #6 through #10 and ex. cr.

You're welcome to work on the second half of this week's problems on your own, but we'd encourage you to do so with a partner, if you'd like. If you do work in a pair, be sure to work at the same physical place and to trade off at the keyboard/debugging roles, as described in the syllabus.

Whether you work on your own or with a partner, you should submit your definitions for these functions in a file named part2.rex.

Problem 6:    our informal CS 60 questionnaire, online

[10 points, pair or individual]

For this problem, you'll create a webpage whose URL is

http://www.cs.hmc.edu/~yourloginid/cs60q.html
that answers a few questions we did not have time to ask in CS 60's first class.

First, feel free to peruse
http://www.cs.hmc.edu/~hadas/cs60q.html
http://www.cs.hmc.edu/~zdodds/cs60q.html

Creating your own such page will reinforce some of the unix commands you've already used and it will introduce a couple more, as well.

  • At knuth's command prompt, go to your home directory with cd ~.
  • If you use ls -F to examine the contents of your home directory, you should see a subdirectory named public_html/.
  • Go into that subdirectory with cd public_html.
  • Copy one of the above webpage files to your own directory:
           cp /home/zdodds/public_html/cs60q.html .
       of  cp /home/hadas/public_html/cs60q.html .
      
  • Edit that file to customize it for yourself... the basic questions are
    • your name
    • What's a place outside of HMC/Claremont that you do (or have) considered home?
    • Your favorite _________ is __________ ?
    • Your least favorite _________ is _________ ?
    • Mention if you have any programming background (Python is probably part of it, but there may be more...)

  • Once that file is edited and in place, it still will not be viewable until you change its permissions. The file needs to be world-readable, and the command to allow everyone access to it is this:
            chmod ugo+r cs60q.html
           
  • Now, you should be able to point any browser to the URL
            http://www.cs.hmc.edu/~yourloginid/cs60q.html
      
    and it will be visible.
  • If you have experience building web pages/applications, feel free to add bells and whistles. But, there's no need to do so. Also, if you'd prefer not to have this page hanging around, feel free to get rid of it -- but we'd ask you to wait until this hw has been graded... .

Note that, although you're certainly welcome to work on this with a partner, do create these personalized webpages for each person.


Problem 7:    enum

[10 points, pair or individual]

Write a function enum( L ), taking any list L as input. Then, enum( L ) should return a list of two-element lists, or pairs that enumerates the elements of L, starting from zero. These examples will show the intent:

test( enum( ["jan","feb","mar"] ),
      [ [0,"jan"], [1,"feb"], [2,"mar"] ] );

test( enum( [2008,2009,2010,2011] ),
      [ [0,2008], [1,2009], [2,2010], [3,2011] ] );

test( enum( [] ),
      [] );   
Remember that spacing/indentation is less important in Rex than Python. The semicolons show where the three test expressions, above, end.

Hint!: Write a helper function of two inputs that does all of the work. Then, have enum delegate its work to that helper function with suitable arguments. That is, write

     enum( L ) => enumHelper( two inputs here! );
Then, all of the rest of the work, including recursion, will be in defining enumHelper. The original enum will never get called again... .


Problem 8:    removeAll

[10 points, pair or individual]

Write a function removeAll( e, L ) taking as inputs any datum e and any list L. Then, removeAll( e, L ) should return a list identical to L, but with all top-level instances of e removed.

Here are some examples:

test( removeAll( 1, [1,1,42,1] ),    [42] ); 
test( removeAll( 1, [[1,1], 1] ),    [[1,1]] );
test( removeAll( 1, [2,3,5,7]  ),    [2,3,5,7] );

A good place to start for this is the rmv1 function we examined in class...


Problem 9:    uniq

[10 points, pair or individual]

Write a function uniq( L ) taking in any list L as input. The output should be a list like L, with all top-level, later-appearing duplicate elements removed. For example,

test( uniq( [1,2,3,1] ),   [1,2,3] );

test( uniq( [1,1,2,1,1,2,1,2,3,2,1,3] ),    [1,2,3] );
Note that Rex does have a built-in function named remove_duplicates that is identical to uniq. As with many of the examples in class, the idea here is to implement uniq using recursion - not just to delegate to remove_duplicates.

Hint: the built-in member function could be helpful, and you're welcome to use that one... . Except in cases where you're actually implementing a built-in function, you're welcome to use Rex's built-in functions.


Problem 10:    wordScore

[10 points, pair or individual]

This problem introduces the action-packed built-ins, explode and implode, as well as the useful built-in assoc function. In addition, you'll use the ability to import files with the *i command within a Rex source code file. You'll compose these pieces in order to create a scrabble-scoring function named wordScore.

You'll want the data in the file scrabbleScores.rex: it maps characters to their Scrabble point values. You should get that file by copying to your current directory with the following command:

    cp /cs/cs60/hwfiles/a1/scrabbleScores.rex .
Again, that period at the end of the command says that the current directory is the destination directory.

Either at the rex prompt or at this point in your part2.rex file, you can include the line

*i scrabbleScores.rex
As long as scrabbleScores.rex is in the directory where you are working, this will include its code at that point in your file. If you open the scrabbleScores.rex file, you'll see that it contains a list of [ character, score ] pairs. That list is named scrabbleScores.

Association lists, or "A-lists," are lists-of-lists like scrabbleScores. They're often used as data repositories, accessed by the built-in function assoc, usually pronounced "a sock." assoc( e, AL ) simply looks up the datum e in an association list AL. It returns the first member of AL whose first element is e, if one is present, or the empty list [], if not. For example,

rex > assoc( 1, [ [0,"jan"], [1,"feb"], [2,"mar"], [1,"apr"] ] );
[1, feb]

rex > assoc( 42, [ [0,"jan"], [1,"feb"], [2,"mar"] ] );
[]

rex > *i scrabbleScores.rex      // loads in the file
read file scrabbleScores.rex        // we can now use scrabbleScores

rex > assoc( 'q', scrabbleScores );
[q, 10]

rex > assoc( 't', scrabbleScores );
[t, 1]

With this as background, write a function wordScore( w ) that takes in a string named w and returns that string's scrabble score, i.e., the sum of the scores of its individual characters. For example,

test( wordScore( "zap" ), 14 );
test( wordScore( "zzz" ), 30 );   // We ignore the fact that Scrabble has limited tiles...
test( wordScore( "aah" ), 6 );
test( wordScore( "twelve" ), 12 );
test( wordScore( "fortytwo" ), 17 );
Note that you might use is_string for your wordScore function in a similar manner to the way you used is_list for the superreverse function! That way, wordScore could handle both strings and lists as input.

Notes on strings vs. characters: explode and implode

Unlike Python, Rex (and almost every other computer language: Java, Prolog, C, C++, ...) distinguishes between characters and strings. Characters are always a single item and are surrounded by single quotes, such as 'a', 'b', or ' ' (the space character). Strings are a collection of zero or more characters and are surrounded by double quotes, such as "this is a string!", "" (the empty string), or "a" (a string with one character). Double-quoted strings are not the same as single-quoted characters, even if the string contains only one character!

You can convert back and forth between strings and lists of characters with explode and implode. For instance,

rex > explode( "star" );  // a supernova?
['s', 't', 'a', 'r']         // note: rex doesn't print the single quotes here!

rex > implode( [ 's', 't', 'a', 'r' ] );   // a black hole?
"star"                       // rex won't print these double-quotes, either

rex > explode( "a" );
['a']                

rex > implode( [] );
""
The single- and double-quotes are included above for clarification; Rex does not print them.

Hint: writing a function that returns the value of a single character will help here. The assoc function is useful, but not exactly what is needed... .



Totally optional extra credit...

You're welcome to try these on your own or in a pair. They've been described as "challenging and fun" by unbiased reviewers (Prof. Ran).

In addition, the first problem below, #11, will be on the HW next week. If you succeed at it this week, you earn credit for it both times... .


Optional extra credit ~ problem #11:    bestWord

[up to +10 points, pair or individual]

This problem is a larger task: it asks you to write bestWord( rack, Words ), where rack is a string representing one's available scrabble tiles and Words is a list of legal words. Then, bestWord should output a [word, score] pair, where word is the highest-scoring member of Words such that word can be formed from the letters in rack. Use the previous problem's wordScore function. If Words has no elements that can formed from rack, your function should return ["no word", -1]. You may break ties however you like, but should return only one [word, score] pair.

Here are some examples:

test( bestWord( "abcdefg", [ "bbb", "bag" ] ), [ "bag", 6 ] );
test( bestWord( "abcdbbg", [ "bbb", "bag" ] ), [ "bbb", 9 ] );
test( bestWord( "a", [ "bbb", "bag" ] ),       [ "no word", -1 ] );
test( bestWord( "abcdefg", ospd3 ),            [ "fed", 7 ] );
test( bestWord( "zabcdefg", ospd3 ),           [ "fez", 15 ] );
Note that each letter in the rack may be used only once. Best words with the same highest score are OK, too.

The file threeLets.rex contains a list of words named ospd3: these are all of the three-letter words from the Official Scrabble Players' Dictionary. Remember that, to use it, you'll need
   *i threeLets.rex
with a copy of that file in your current directory.

Planning out a strategy for this problem before diving in to the coding is a good thing! You will want to define and use a number of helper-functions to keep your definition of bestWord simple and elegant. You'll want to keep the helper-functions short, as well! Our solution's functions use a total of six rules, not including wordScore and rmv1, which are helpful. If you're getting toward 15 or more rules, you might consider redesigning your solution!


Optional extra credit ~ problem 12:    multLists and mult

[up to +15 points, pair or individual]

I have always considered the infinite-precision integer arithmetic that some languages offer to be a neat (if not always useful) feature. Although rex does not offer infinite-precision integer arithmetic, it's surprisingly succinct to implement!

For this extra credit problem, your task is to write two functions that perform integer-precision multiplication of positive integers: mult( str1, str2 ) takes in two strings representing positive decimal integers and should output a string that is the product of those input integers. For example,

test( mult( "6", "7" ), "42" );

test( mult( "123456789", "987654321" ), "121932631112635269" );

En route to the mult function, you will find it helpful to write multLists, which does the same thing for reversed lists of single-digit integers:

test( multLists( [6], [7] ), [2, 4] );

test( multLists( [1,2,3,4,5,6,7,8,9], [9,8,7,6,5,4,3,2,1] ), 
      [9,6,2,5,3,6,2,1,1,1,3,6,2,3,9,1,2,1] );
Having them reversed makes implementing the "elementary-school" multiplication algorithm much better!

Certainly, you'll use explode and implode here. Also, you might consider writing addLists (similar to multLists). We also found functions for converting back-and-forth between characters and single-digit integers helpful. The built-in make_number( c ) returns an integer when given a character c. Going the other way, we used

// from single-digit integer x to a character, by indexing into a list:
chr(x) => ['0','1','2','3','4','5','6','7','8','9'](x);

Warning!    The built-in function make_string does not work -- this is why we make the chr suggestion, above (from which you'll be able to create your own make_string...).



Submitting your work

Submission of your part1.rex and part2.rex files provides an opportunity to get comfortable with file-transfer to and from from servers like knuth.

Password?    Remember that your default password to the submission site is the default four-letter one mentioned in class (drop us an email if you don't remember it...). Once you get into the submission system, you can change your password to be whatever you'd like (such as your ordinary password!)

Submitting pair programs:    Partners who have created one file together should each submit that file, under their own names. You may have to close the browser and then re-open it, in order to log in as the other person.

Friendly warning: be sure you've tested your files after adding any final comments or notes... it's amazing how often a comment character gets omitted accidentally and then code fails to work unless its was tested just before submitting!

    Submitting a file from the CS Lab Macs    If you are on a lab machine, you can simply submit using our online site -- skip down to the "Web submission" instructions below.

On the other hand, if you're onyour own machine, you'll need to transfer your files from knuth to your machine. The instructions differ based on your operating system:

    Copying a file from knuth to a local Mac    First, while on knuth, type pwd in order to see the full path name to your part1.rex and part2.rex files. For example, if your login were penguin, then your path might be

/home/penguin/cs60
Then, open a new terminal window on your local machine and don't login to knuth. Rather, type cd Desktop and hit enter in order to put you in your Desktop's folder (via the command-line, admittedly).

From that local terminal (now at your Desktop), use the secure-copy program, scp, by typing

scp penguin@knuth.cs.hmc.edu:/home/penguin/cs60/part1.rex .
where you replace penguin with your knuth login name! The colon between the machine name and the pathname is important, as is the period at the end -- it tells the command to copy part1.rex to your current directory, which is probably your Desktop. If all goes well, you'll see that file appear on your Desktop. Now you'll be able to submit it via the web (and you'll have a backup copy in case an errant command deletes the one on knuth!)

Just as an aside, you can copy files to knuth with scp, too. It would look like this:

scp myfile.txt penguin@knuth.cs.hmc.edu:/home/penguin
tailored to suit your desired filename and destination directory, of course!

    Copying a file from knuth to a Windows PC    This is even easier than on a Mac because of the convenient free program named WinSCP, available for download from http://winscp.net/eng/download.php -- I always use the "portable executable" link a little way down on the left.

Once you've downloaded that program, perhaps to your desktop, double-click it and then input knuth.cs.hmc.edu as the "Host name" and then hit "Login" button at the bottom. The program will prompt you for your login name on knuth and then your password. After that, you will be presented with a two-panel view of your local files as well as your remote files (the ones on knuth). Dragging and dropping between the panels (or to your local desktop) works as you'd expect.



Web submission

Once your file is on your local desktop (or a subfolder), navigate to the CS 60 submission page and log in. You should then be able to click on the "Submit a File" button, choose your file (from its spot on your local machine), and upload it. You can check on your submitted files (and, later, grades) from the "Browse Files" button.

Be sure to submit both part1.rex and part2.rex under Week 1 at the submission site.

You may submit your work multiple times -- only the file submitted latest (before the deadline) will be graded, but all of them will be saved in case you or we need to access them for any reason.

Problems submitting? Feel free to email your files/questions/concerns to dodds@cs.hmc.edu.




Link to the CS 60 submission site
Back to the top-level assignments page
Back to the top-level CS 60 page