Work through the following lab, and finish up in your own time if you need it. Turn in your code for Part 6 #3 within a week. Keep in mind that in addition to this lab, there are SML resources linked on the course page if you need help. You may turn this in in pairs.
The questions that you find intermixed throughout the lab are for you to think about. Ask in lab or in class if the answers aren't in clear.
Start up emacs by typing
emacs &
Open up a Linux shell inside emacs by typing alt-x, then type shell at the M-x prompt at the bottom of the screen. Type sml at the Linux prompt you see.
In your emacs window, you should see the following:
Standard ML of New Jersey, Version 110.0.7, September 28, 2000
[CM; autoload enabled]
-
Type in a very simple ML program just to try it out. At the prompt, enter in the code
val x = 3;
SML should return the following:
val x = 3 : int
Change your program in some way. Use alt-p to move backwards through your command history (alt-n will move forwards), and change it to read
val x = 8;
To exit SML, enter ctrl-d. Enter ctrl-d again to log out from the Linux prompt. You should see a message that says "process sml finished". Finally, to exit Emacs, type ctrl-x, ctrl-c.
Start up Emacs and SML again. At the SML command prompt, enter the following commands:
val numbers = [6,4,9,2];
val fruits = ["apple","orange","pineapple"];
What "type" do the brackets define?
(By the
way - if garbage collection messages looking something like
GC #0.0.0.0.1.12: (0 ms)
start bothering you, you can enter
SMLofNJ.Internals.GC.messages(false);
to make them go away.)
2. Enter in the following:
hd ["apple","orange","pineapple"]; tl ["apple","orange","pineapple"];
What do the functions hd and tl do?
The response that you get from SML indicates the answer in a variable called it, as well as a type (either string, or string list). Why is the type different for the functions hd and tl?
Write a sequence of hd and tl that will pick the string "pear" out of the following expression.
["apple", "orange", "pear", "grapefruit"] [[["apple"], ["orange"], ["pear"], ["grapefruit"]]]
Execute the following statements. The last three produce errors. Why?
(* This is a comment, by the way! *) 1::[2,3]; "apple"::["pear","orange"]; [1,5]@[2,3]; ["apple","grapefruit"]@["pear","orange"]; [2,"apple"]; 2::["apple","orange"]; "apple"::[5,7];
When are lists equal? Experiment and find out. For example, what happens when you enter the following?
[1,2]=[1,2,3];
Execute the following code. What do length and rev do?
length ["plato","socrates","aristotle"]; rev ["plato","socrates","aristotle"];
Entering your code interactively is fun, but not a good idea for creating large programs. A better way to go is to create a file with your code, save it, then run it inside SML. We do this in exactly the same way we did with Scheme:
In the terminal window, make a directory to store your code for this class in. For example, you can type from your home directory
cd cs217
Create a subdirectory from here by typing
mkdir sml
Then change to this directory by typing
cd sml
Open an Emacs window by typing
emacs &
at the terminal window command prompt.
To start working on a program, type ctrl-x, ctrl-f (the f means find), and provide Emacs with a filename. For example, I might type prog1.sml. Emacs will open up this file if it exists, or will present you a new blank file.
Start typing in some SML code. Use any of the above examples that you wish. When finished, save your program by typing ctrl-x, ctrl-s (the s means save).
Open up a second window inside emacs by typing ctrl-x, 2 (the 2 indicates two windows). To switch between windows, you can either use the mouse or enter the command ctrl-x, o (the o means "other"). Switch to the bottom window, enter alt-x, and at the command prompt type the word shell. Change to the directory in which you stored your SML program. For example, if I stored my Scheme program in the directory ~/cs217/sml, I would type
cd ~/cs217/sml
Startup SML by typing sml in the shell window.
Run your program. To do this, type
use "prog1.sml";
(or substitute here whatever you named your program).
You should generally use this approach for entering and running SML code, but entering code directly into the shell window is good for testing out quick ideas.
Try running the following SML code:
fn x => x + 1;
What does SML return? fn and the => operator return a function without a name. How does this compare to Scheme?
A function without a name is useless, as it is immediately garbage collected. Try this instead:
val addOne = fn x => x + 1;
(Use the "tab" key to indent your code. Emacs will indent all your code correctly, automatically.)
Then try:
addOne 5;
The val statement created a pointer to a function, called addOne, which refers to the function you just created.
The above can be entered via shorthand as
fun addOne x = x + 1;
Try it! Remember, this is just shorthand for the slightly longer code above.
Try this:
val anotherAddOne = addOne;
anotherAddOne 5;
At the pointer level, what is happening here? Draw a picture on this paper indicating what is happening.
Try the following code.
let val x = 10 in x * x end; x;
What does letdo? Why do you get an error when you try to see the value of x after the "let block" has ended?
Enter and load the following function. What does it do? Why?
fun lessOfAMystery(L) = if L = nil then L else lessOfAMystery(tl(L)) @ [hd(L)];
Note that I've used parentheses around the function arguments. This is optional (in the cases we have seen so far).
Write a recursive function, keepFirstN, that returns a list of the first n elements in a list. You may assume that there are at least n elements.
Example:
- keepFirstN(3,["a","b","c","d","e","f","g","h","i"]); val it = ["a","b","c"] : int list
The following code does something familiar. Type it in, and try it. Why does it work? What is it doing?
fun mystery([]) = [] | mystery(start::rest) = mystery(rest) @ [start];
Here are a few more examples of pattern matching. Try them and figure out what's going on. What does the underscore do?
fun fact(1) = 1 | fact(n) = n*fact(n-1); fun last([]) = ~1 | last(first::nil) = first | last(_::rest) = last(rest);
Rewrite keepFirstN using pattern matching. Submit your code in a file called keepfirstn.sml.
let fun loop(count) =
(
print("Count = ");
print(Int.toString(count));
print("\n");
if count < 10 then
loop(count+1)
else
count
)
in
loop(0)
end;