Pre-tutorial apologies...the tutorial is long. But this stuff can be confusing if it's not clearly explained, so I tried my best.
PLT Scheme/Racket is a mainly functional programming language I was introduced to during my third high-level software programming class in college. It has support for many different paradigms, including object-oriented programming, logic programming, and web applications. If you've never programmed in LISP (or any dialect thereof), then it might be a challenge at first, as the syntax will probably be unfamiliar to you, since it differs a lot from C/C++ and their ilk.
Alright, well, for this tutorial, you'll need to head over to http://download.plt-scheme.org/ to get the version I'm using (4.2.5). On Racket's website, it says that backwards compatibility is maintained, although I don't have the DrRacket IDE...I have DrScheme, which is the older one. The layout of the IDE might be slightly different, but I can't see it being a HUGE overhaul. DrScheme was a relatively simple IDE as far as IDEs are concerned, so I can't see you having too much trouble if you download DrRacket instead.
If you'd like to be up to date and use DrRacket, head on over to http://racket-lang.org/download/ and get the one for your system. It's a simple installer program.
Setting up the language
When you fire up DrScheme, click the "Language" menu option, select "Choose Language...", then select the top option, which should be "Use the language declared in the source". This will allow you to directly control what language you write your code in by specifying it in the source code file.
Now, you're pretty much all set up. If you've ever worked with Java, you'll be thinking, "What? I didn't set up the classpath (or Scheme/Racket equivalent)!". You don't have to. DrScheme/DrRacket knows where it is, since it was packaged with the install of Scheme/Racket itself.
The output from your programs will appear in the bottom window of DrScheme. If there is just one main window, then the bottom window that I'm talking about will probably appear on output.
A quick little note here...comments in PLT Scheme/Racket start with a semicolon. Everything past the semicolon will be ignored as far as the code is concerned. You can also do block comments with #| where you want to start it and |# where you want to end it.
; single line comment #| multi-line comment multi-line comment |#
When specifying the language in the source code file, you need a special line at the beginning of the file. It looks like this.
That will let DrScheme/DrRacket know that you're using the Scheme language.
A typical statement, or function call, will ALWAYS be encased in parentheses in PLT Scheme/Racket. This comes from LISP.
Well, now I'll start off with something simple. A little function to output something to the standard output. We're going to use the print function. This function takes one argument, whatever you want to output.
(print "Hello, world!")
Notice now that there is only a space between the function name and the argument(s). This will output "Hello World" to the standard output, INCLUDING THE QUOTES. That is important. When strings are used for output, the quotes come with them.
Now how to declare variables. For this, we use the define keyword.
(define x 5)
You C/C++/Java/C# guys might be thinking, "Hey, he didn't specify a data type!". Well, you don't have to. As a matter of fact, I don't even know if you can. I just know that you don't. This statement will make x equal to 5. Note that this is just like a function call. With the parameters being the name of the variable and the value to give it.
; these are also valid examples (define x 1) (define x 3/5) ; set x to .6 (or three-fifths as Scheme will represent it) (define x "YES!") (define x 5.284856932) (define x #t) ; set it to true (define x #f) ; false
x can be any data type. You can also change the value later with the set! function. You can change it to any type that you require. This can be a blessing or a curse, so watch out and make sure you're not operating on something incorrectly.
The set! function is used exactly like define.
(set! x 5) ; set x to 5
The only stipulation is that whatever variable you're setting is already declared using define.
Now, I'll cover the basic mathematical functions. They're still like any regular function call, just with more argument capabilities.
(+ 5 10) ; yields 15 (- 10 5) ; yields 5 (* 3 4) ; yields 12 (/ 5 10) ; yields 5 over 10, or one half
Notice that there are just spaces between the arguments, no commas, nothing.
Now...these functions have the capability to take an infinite amount of arguments...
(+ 1 2 3 4 5) = 1 + 2 + 3 + 4 + 5 = 15
(- 5 4 3 2 1) = 5 - 4 - 3 - 2 - 1 = -5
(* 2 3 4 5) = 2 * 3 * 4 * 5 = 120
(/ 3 5 6 4) = 3 / 5 / 6 / 4 = 1 / 40
You could have TONS of arguments, but I used 4 or 5 to illustrate how the formula works.
Now, if we wanted to combine what we've learned so far, we can print out the result of a formula.
(print (* 2 3)) ; prints 6
It's simply supplying the multiplication function result as a parameter to the print function.
Now let's move on to an example summing up all of the things we've learned so far...
#lang scheme (define x 5) (define y 4) (define z (* x y)) (print z) ; prints 20
As the comment says, this will print 20 by assignment to a variable and then printing said variable.
Each function call will be in its own set of parentheses. That's essentially the key thing to remember. The program comprises several function calls.
Chances are that you already know what a function is. It's a piece of code that can be used multiple times to perform a task. The way to implement them in Scheme/Racket is as follows. We use the define keyword again.
(define (functionName) (print 4))
The design is up to you. You can leave it all on one line, or indent it how you wish. For this, either way is fine, but more complicated functions may get confusing if a proper organization technique isn't met.
As you can probably tell, the functionName would be your function's name. This function actually takes no parameters. It will just print the number 4 whenever it is called. However, note that the entire function declaration and body are encased in the parentheses belonging to define.
The function will return whatever the statement inside it returns. Since the print command returns #<void>, if you had a statement like this -- (define x (function)), x would be set to #<void>, since the print command is used to output, and not to return a useful value.
Now I'll now demonstrate how to define a function that takes two parameters (assumed to be numbers), adds them together and returns the result. There are two ways to write this. Each code box will display one.
(define (add x y) (+ x y))
(define (add x y) (define result (+ x y)) result)
x and y are the parameters. You can name them whatever you want. Just know that the syntax uses spaces between each parameter.
Personally, I would use the first way. The only difference between them is that the second one uses a local variable (used the same way as a regular one, however, it can only be used inside of the function).
Most of you are probably familiar with if structures. Based on "if this is true, then do something...otherwise do something else". Scheme has the same type of thing:
(if (condition) (return this) (return that))
The common comparator functions (less than, greater than, equal, etc.) that return a boolean true/false value are as follows, and are much the same as they are in C/C++:
- = (for numbers)
- eq? (similar to .equals() in Java, it's used for comparing objects other than simple data types)
The only one of the above functions that doesn't take two arguments for comparison is null?. Other than that, they all take two arguments, and compare them and return the value representing the comparison.
It's a convention to name a boolean function that would be used for something like this with a question mark at the end. This is a flag for the programmer's sake. The function, in this case, is typically called a predicate.
(if (> 5 4) "5 is greater than 4" "4 is greater than 5")
The condition is translated to if 5 is greater than 4, return "5 is greater than 4", else return "4 is greater than 5". Needless to say, the "else" portion will never be returned. Whatever you return can be functions and whatever else you want them to be.
Moving to a structure like the if...there's one in Scheme called cond. It's like an if/else if/else if/else type of structure. There can be an infinite number of conditions.
(define x 10) (define y 5) (cond ((= x y) "equal") ; one condition/return value ((> x y) "x > y") ; another ((< x y) "x < y") ; another (else "something went wrong")) ; theoretically unreachable
Well, I think that's enough for the first part. I'll get another one up soon going over some other basic things, and possibly some more advanced computation using the syntax of Scheme.
You may be thinking...hey, where's the stuff about loops? Well, there are no loops. You can use recursion and lists, which are a major part of LISP. After all, LISP stands for LISt Processing. You can achieve anything you'd like using lists and recursion (among other things, depending on the needs). It's an odd way of thinking, however, once you get used to it...it's not so bad. You'll probably struggle with the shear amount of parentheses at some point, but you'll be able to see them clearly once you have some experience.
For some practice, you can head over to Martyr2's Mega Project Ideas List for some basic application practice, or over to Project Euler for some practice problems.
Functional programming allows you to write some very elegant solutions to some complex problems.
Thanks for stopping by! Suggestions for any improvements are completely welcome!