Scheme used to be the first language CS majors at MIT, UC Berkeley, and other noteable institutions would be taught. Today, a number of those CS programs start with Python.
Scheme is noteworthy for a number of reasons that are beyond the scope of this lesson. It has a minimal syntax, which makes getting started easy.
On to our obligatory first exercise in a new language: Writing a program to print
Hello, world!
. We'll leverage an online Scheme interpreter to run our programs. Open
http://repl.it/languages/Scheme
in a new tab.
The interactive BiwaScheme interpreter hosted at http://repl.it will interpret each statement as it's entered. Try typing this:
Hello, world!
You should have gotten an error, as raw text is not a valid statement. How about this?
"Hello, world!"
The expression above has a value , as a string. The value is echoed (printed) back to you by the interpreter, which parses your statement and prints the resulting value.
If you type
x
and press [Enter], you're informed that
x
is an
unbound symbol
, meaning that the variable
x
isn't tied (bound) to any value. Let's declare and initialize the variable
x
:
(define x 2)
What's stored in
x
? Type this statement to see:
x
We stored a number in
x
. How about strings?
(define words "I like cat food.")
words
We didn't have to declare that
x
and
words
would hold numbers and strings, respectively. That's because Scheme determines the data type that is needed for us. That makes Scheme
loosely typed
, whereas Java is referred to as
strictly typed
, meaning we have to specify the data type for variables we use.
In Java, we used basic arithmetic operators --
+, -, *, /
-- quite a bit. We typically have expressions like
2 * 3
as in this Java statement:
System.out.println( 2 * 3 );
Scheme has a different order for operators and operands. While in Java a binary operator (like
*
) requires the order by
operand_1 operator operand_2
, Scheme's syntax requires the operator to go first. Try these:
(* 2 3)
(+ 5 -1)
;comments start with semi colons
;you can nest these statements:
(+ (* 2 3) (- 8 6))
;just like in math, innermost ()s are evaluated first
;are 3 operands OK?
(+ 1 2 3)
You might notice the Scheme interpreter helping you make sure you close all open parentheses by highlighting matching pairs of parentheses as you type. One of the frustrating features of Scheme for beginners is the number of parentheses that are sometimes needed for complex statements. Stay tuned...
{ 52, 29 }
{ 52, 29 , 106 }
{ 52, 29 , 106 }
(* (* 4 3) 2)
In Java, we often talk about
methods
as sets of instructions that deal with input data (an argument, value in a field, etc.) and have some kind of effect like changing a field's value or returning data (object or primitive value). In Scheme, we define functions. (There's a longer form that involves the use of
lambda
that I'm not including here.)
Here's an example of a function that takes in a value that we'll call
n
and returns that number times 5:
(define (timestwo n) (* 2 n))
You call a function in this manner:
(timestwo 6)
;printed result is 12
You can even write a function in terms of another:
(define (timeseight n) (* (timestwo n) 4))
;call the function:
(timeseight 3)
if
and
cond
are common conditionals used in Scheme.
if
In Java, we may have
if()
used in this way:
int x = 10;
if( x < 16 ) {
System.out.println("x less than 16");
} else {
System.out.println("x is at least 16");
}
Here's the equivalent set of statements in Scheme:
(define x 10)
(if (< x 16) (print "x less than 16") (print "x is at least 16"))
For readability, you might use multiple lines to write the
if
statement:
(if (< x 16)
(print "x less than 16")
(print "x is at least 16"))
As you might have surmised, the statement to run if the condition is true appears right after the condition, while the "else" statement appears next. Here is Scheme's syntax for
if
statements:
(if (condition) (then ...) [(else...)] )
Note that the
else
portion is in square brackets since it's optional -- just like
else
isn't required for
if()
statements in Java.
cond
Sometimes there are more than just two cases we need a program to react to. In Java,
if()
handles one case (condition is true), while
if()-else
handles two cases (condition is true or false). When there are more cases, Java provides
if()-else if()-else
. Scheme can do similar using nested
if
statements:
(define x 0)
(if (< x 0)
(print "x is negative")
(if (> x 0)
(print "x is positive")
(print "x is zero")))
Yuck! There has to be a better way. Enter
cond
:
(define x 0)
(cond
((< x 0) (print "x is negative"))
((> x 0) (print "x is positive"))
(print "x is zero"))
Notice now the last condition doesn't have a boolean like
(= x 0)
-- it's treated as an
else
. And that "else" is optional.
See another example of
cond
here
.
One of the greatest strengths of Scheme is the ease with which recursion can be expressed. Consider Fibonacci numbers, which are the elements of the set
{ 1, 1, 2, 3, 5, 8, 13, ... }
.
Earlier this year, we wrote a recursive method to find the
/*
* Find the n^th Fibonacci number.
* Precondition: n >= 0
*/
public static int fibRec(int n) {
//base cases:
if ( n == 0 || n == 1 ) {
return 1;
}
//this fib # is sum of the two prior fib #s:
return fibRec(n-1) + fibRec(n-2);
}
Simplifying the method to make our job in Scheme a little easier, we get
public static int fibRec(int n) {
if ( n < 2 ) {
return 1;
} else {
return fibRec(n-1) + fibRec(n-2);
}
}
Try this in Scheme:
(define (fib n)
(if (< n 2)
1
(+ (fib (- n 1)) (fib (- n 2)))))
;does it work? expect { 1, 1, 2, 3, 5 }
(fib 0)
(fib 1)
(fib 2)
(fib 3)
(fib 4)
Want to write a program that counts down from
n
to 1? We'd typically write Java like so:
public static void countDownFrom(int n) {
while( n > 0 ) {
System.out.println( n );
n--;
}
}
If we considered a recursive version of the method, it might look like this:
public static void countDownFromRec(int n) {
System.out.println( n );
//base case:
if( n == 1 ) {
return;
}
countDownFromRec( n - 1 );
}
In Scheme, we'd typically write a method that calls itself (recursion) to accomplish this task:
(define (countDownFrom n)
(if (= n 1)
(print 1)
(begin
(print n)
(countDownFrom (- n 1)))))
(countDownFrom 5)
(Note the use of
begin
, which groups multiple statements together to run one after the other.)
Since counting down is typically easier for recursion, counting up is presented afterward. It's best for the user to call the procedure like this --
(countUpTo 10)
-- but in actuality the procedure will need to take two arguments. This is where a helper procedure comes in handy:
(define (countUpTo n)
(countUpToHelper 1 n))
(define (countUpToHelper a n)
(if (= a n)
(print a)
(begin
(print a)
(countUpToHelper (+ a 1) n))))
(countUpTo 10)
Write a function that converts a degree measure to radians:
(define (deg->rad deg) ???)
(deg->rad 180) ;approx 3.141592653
(sin (deg->rad 30)) ;0.5
Write a recursive function that returns the sum of squares up to
n
:
(define (sum_of_squares n) ???)
(sum_of_squares 5) ;55, the value of
;1^2 + 2^2 + 3^2 + 4^2 + 5^2
Write a recursive function that raises a base to a power:
(define (exp a b) ???)
(exp 2 5) ;32, value of 2^5
(exp 3 0) ;1, value of 3^0
Scheme has a built-in function for modulo. Try it:
(mod 14 4)
. Write your own modulo function:
(define (% m n) ???)