Scheme language elements
Comments
Comments are preceded by a semicolon (;) and extend for the rest of the line. NexJ Studio's scheme editor provides some useful shortcuts for working with comments. These include:
- Generate Element Comment: Alt+Shift+J
- Toggle Comment: Ctrl+Shift+/, which works with multi-line selections
; this is a comment in scheme
Expressions and types
The simplest expressions are constant atomic symbols, numbers, Booleans, characters, strings, vectors, pairs, and lists.
Symbols, procedure and variable names are case-sensitive.
> "hello" ; constant string ; "hello" > 12345.67 ; constant number ; 12345.67 > 'sym ; symbol ; sym > '(a b c d e) ; list ; if we didn't have the ' that is, if the expression was "(a b c d e)", then 'a' would be evaluated as the function 'a' with arguments b c d and e. ; (a b c d e) > '(a . b) ; pair ; each pair has a "head" (in this case a) and a "tail" (in this case b). (a . b) > #f ; boolean false - true is #t ; #f
The Boolean type represents true and false by #t and #f respectively. Any type can be used where a Boolean type is expected; any value other than #f is considered to be true, including the empty list.
Scheme uses a prefix notation. For example:
> (+ 3 4) ; 7 > (format "Mailbox {0} has {1} new messages." "shawed1" 55) ; "Mailbox shawed1 has 55 new messages." > (string-upcase "shout") ; "SHOUT" > (> 5 7) ; #f
Scheme's basic data structure is the list. Lists are really lists of pairs. A pair is written as (val1 . val2)
. A list is a linked list of pairs, ending in null. The equivalent notation for null is (). For example, '("a" "b" "c")
is really ("a" . ("b" . ("c" . ())))
which also could be thought of as ("a" -> ("b" -> ("c" -> (emptyList))))
. Lists are written as sequences of objects surrounded by parentheses.
> '("a" "b" "c") ; this is a quoted list, the ' is equvalent to quote. For example, (quote ("a" "b" "c")) ; ("a" "b" "c") ; The quote makes Scheme treat the list as data. Without the ' character, Scheme would try to treat "a" as a function and would throw an error.
There are many functions for manipulating lists, including accessors: getters car
and cdr
and setters set-car!
and set-cdr!
. The function list-ref
provides access to an arbitrary member of a list, length
gives its length, and the list constructor is list
. There are also procedures to reverse a list, to obtain the tail of a list, to check for list membership, and to perform key-value lookups (association lists).
> (car '(1 2 3)) ; get the head of the list (first pair) ; 1 > (cdr '(1 2 3)) ; get the tail of the list (first pair which returns the rest of the list) ; (2 3) > (cdr '(1)) ; get the tail of the list (rest of the list in this case is null ; () > (cons '(1 2) '(3 4)) ; construct a new pair ; ((1 2) 3 4) > (list '(1 2) '(3 4)) ; construct a list ; ((1 2) (3 4))
Variables
Variables are dynamically typed and are bound to values by a define
, a let
expression, and a few other Scheme forms. Variables bound at the top level with a define
are in global scope.
(define startTime (now))
Variables bound in a let
are in scope for the body of the let
.
(let ((startTime (now)) (index 0)) ... ; Scope of startTime and index. ... )
A let*
expression is used when one of the declarations in a let depends on another.
(let* ((lowerBound 1) (upperBound (+ lowerBound 100))) ... ; Scope of lowerBound and uppderBound ; to start with lowerBound = 1 and upperBound = 101 ... )
Functions
A function is a portion of code within a larger program that performs a specific task and is relatively independent of the remaining code. A function can take zero or more arguments, does some processing in its body and returns a result which is the return value of the last statement executed before exiting the body.
In Scheme, unnamed functions are created with the special keyword lambda
.
(lambda (x) (* x x)) ; create a function that takes a single argument. ; "lambda" is evaluated and returns a reference to a function that squares its input parameter ((lambda (x) (* x x)) 7) ; evaluate the head of the list, which returns a function, and apply that function to the parameter "7" -> 49 (define sq (lambda (x) (* x x))) ; create a variable "sq" and assign to it our squaring function (sq 7) ; apply the function associated with the variable "sq" to the parameter "7" -> 49
Functions can be arguments to other functions and be returned by them. They can be assigned to variables and are created by lambda forms. For example:
; Define a function called loadData with two arguments fileName and pageSize ; "..." represents the function body. (define loadData (lambda (fileName pageSize) ... ) )
; A shorter equivalent form to example 1. (define (loadData fileName pageSize) ... )
; Shows how functions are applied ; The function being applied is in the first position of the list while the rest of the list contains the arguments (loadData "NightlyBatch.csv" 500)
; The apply function takes its first argument and applies it to a list of arguments, so the previous function call can also be written (apply loadData (list "NightlyBatch.csv" 500))
In Scheme, functions are divided into two basic categories: procedures and intrinsics. All intrinsics are procedures, but not all procedures are intrinsics. Intrinsics are pre-defined functions in the Scheme language. These include +, -, *, /, set!, car, cdr, and other basic procedures.
> (define add3 (lambda (x) (+ x 3))) ; #<PCodeFunction:add3> > (add3 4) ; 7
Assignment
Assignments do not create new bindings, as with let or lambda, but rather change the values of existing bindings. Assignments are performed with set!.
> (define a 1) ; 1 > (set! a 2) ; 2 > a ; 2