This chapter describes Scheme's built-in procedures. The initial (or``top level'') Scheme environment starts out with a number of variablesbound to locations containing useful values, most of which are primitiveprocedures that manipulate data. For example, the variableabs isbound to (a location initially containing) a procedure of one argumentthat computes the absolute value of a number, and the variable+is bound to a procedure that computes sums. Built-in procedures thatcan easily be written in terms of other built-in procedures are identified as``library procedures''.
A program may use a top-level definition to bind any variable. It maysubsequently alter any such binding by an assignment (see4.1.6).These operations do not modify the behavior of Scheme's built-inprocedures. Altering any top-level binding that has not been introduced by adefinition has an unspecified effect on the behavior of the built-in procedures.
Apredicate is a procedure that always returns a booleanvalue (#t or#f). Anequivalence predicate isthe computational analogue of a mathematical equivalence relation (it issymmetric, reflexive, and transitive). Of the equivalence predicatesdescribed in this section,eq? is the finest or mostdiscriminating, andequal? is the coarsest.Eqv? isslightly less discriminating thaneq?.
Theeqv? procedure defines a useful equivalence relation on objects.Briefly, it returns#t ifobj1 andobj2 shouldnormally be regarded as the same object. This relation is left slightlyopen to interpretation, but the following partial specification ofeqv? holds for all implementations of Scheme.
Theeqv? procedure returns#t if:
(string=? (symbol->string obj1)
(symbol->string obj2))
===> #t
Note: This assumes that neitherobj1 norobj2 is an ``uninternedsymbol'' as alluded to in section 6.3.3. This report doesnot presume to specify the behavior ofeqv? on implementation-dependentextensions.
Theeqv? procedure returns#f if:
(string=? (symbol->string obj1)
(symbol->string obj2))
===> #f
(eqv? 'a 'a) ===> #t
(eqv? 'a 'b) ===> #f
(eqv? 2 2) ===> #t
(eqv? '() '()) ===> #t
(eqv? 100000000 100000000) ===> #t
(eqv? (cons 1 2) (cons 1 2)) ===> #f
(eqv? (lambda () 1)
(lambda () 2)) ===> #f
(eqv? #f 'nil) ===> #f
(let ((p (lambda (x) x)))
(eqv? p p)) ===> #t
The following examples illustrate cases in which the above rules donot fully specify the behavior ofeqv?. All that can be saidabout such cases is that the value returned byeqv? must be aboolean.
(eqv? "" "") ===> unspecified
(eqv? '#() '#()) ===> unspecified
(eqv? (lambda (x) x)
(lambda (x) x)) ===> unspecified
(eqv? (lambda (x) x)
(lambda (y) y)) ===> unspecified
The next set of examples shows the use ofeqv? with proceduresthat have local state.Gen-counter must return a distinctprocedure every time, since each procedure has its own internal counter.Gen-loser, however, returns equivalent procedures each time, sincethe local state does not affect the value or side effects of theprocedures.
(define gen-counter
(lambda ()
(let ((n 0))
(lambda () (set! n (+ n 1)) n))))
(let ((g (gen-counter)))
(eqv? g g)) ===> #t
(eqv? (gen-counter) (gen-counter))
===> #f
(define gen-loser
(lambda ()
(let ((n 0))
(lambda () (set! n (+ n 1)) 27))))
(let ((g (gen-loser)))
(eqv? g g)) ===> #t
(eqv? (gen-loser) (gen-loser))
===> unspecified
(letrec ((f (lambda () (if (eqv? f g) 'both 'f)))
(g (lambda () (if (eqv? f g) 'both 'g))))
(eqv? f g))
===> unspecified
(letrec ((f (lambda () (if (eqv? f g) 'f 'both)))
(g (lambda () (if (eqv? f g) 'g 'both))))
(eqv? f g))
===> #f
Since it is an error to modify constant objects (those returned byliteral expressions), implementations are permitted, though notrequired, to share structure between constants where appropriate. Thusthe value ofeqv? on constants is sometimesimplementation-dependent.
(eqv? '(a) '(a)) ===> unspecified
(eqv? "a" "a") ===> unspecified
(eqv? '(b) (cdr '(a b))) ===> unspecified
(let ((x '(a)))
(eqv? x x)) ===> #t
Rationale: The above definition ofeqv? allows implementations latitude intheir treatment of procedures and literals: implementations are freeeither to detect or to fail to detect that two procedures or two literalsare equivalent to each other, and can decide whether or not tomerge representations of equivalent objects by using the same pointer orbit pattern to represent both.
Eq? is similar toeqv? except that in some cases it iscapable of discerning distinctions finer than those detectable byeqv?.
Eq? andeqv? are guaranteed to have the samebehavior on symbols, booleans, the empty list, pairs, procedures,and non-emptystrings and vectors.Eq?'s behavior on numbers and characters isimplementation-dependent, but it will always return either true orfalse, and will return true only wheneqv? would also returntrue.Eq? may also behave differently fromeqv? on emptyvectors and empty strings.
(eq? 'a 'a) ===> #t
(eq? '(a) '(a)) ===> unspecified
(eq? (list 'a) (list 'a)) ===> #f
(eq? "a" "a") ===> unspecified
(eq? "" "") ===> unspecified
(eq? '() '()) ===> #t
(eq? 2 2) ===> unspecified
(eq? #\A #\A) ===> unspecified
(eq? car car) ===> #t
(let ((n (+ 2 3)))
(eq? n n)) ===> unspecified
(let ((x '(a)))
(eq? x x)) ===> #t
(let ((x '#()))
(eq? x x)) ===> #t
(let ((p (lambda (x) x)))
(eq? p p)) ===> #t
Rationale: It will usually be possible to implementeq? muchmore efficiently thaneqv?, for example, as a simple pointercomparison instead of as some more complicated operation. One reason isthat it may not be possible to computeeqv? of two numbers inconstant time, whereaseq? implemented as pointer comparison willalways finish in constant time.Eq? may be used likeeqv?in applications using procedures to implement objects with state sinceit obeys the same constraints aseqv?.
Equal? recursively compares the contents of pairs, vectors, andstrings, applyingeqv? on other objects such as numbers and symbols.A rule of thumb is that objects are generallyequal? if they printthe same.Equal? may fail to terminate if its arguments arecircular data structures.
(equal? 'a 'a) ===> #t
(equal? '(a) '(a)) ===> #t
(equal? '(a (b) c)
'(a (b) c)) ===> #t
(equal? "abc" "abc") ===> #t
(equal? 2 2) ===> #t
(equal? (make-vector 5 'a)
(make-vector 5 'a)) ===> #t
(equal? (lambda (x) x)
(lambda (y) y)) ===> unspecified
Numerical computation has traditionally been neglected by the Lispcommunity. Until Common Lisp there was no carefully thought outstrategy for organizing numerical computation, and with the exception ofthe MacLisp system [20] little effort was made toexecute numerical code efficiently. This report recognizes the excellent workof the Common Lisp committee and accepts many of their recommendations.In some ways this report simplifies and generalizes their proposals in a mannerconsistent with the purposes of Scheme.
It is important to distinguish between the mathematical numbers, theScheme numbers that attempt to model them, the machine representationsused to implement the Scheme numbers, and notations used to write numbers.This report uses the typesnumber,complex,real,rational, andinteger to refer to both mathematical numbersand Scheme numbers. Machine representations such as fixed point andfloating point are referred to by names such asfixnum andflonum.
Mathematically, numbers may be arranged into a tower of subtypesin which each level is a subset of the level above it:
number
complex
real
rational
integer
For example, 3 is an integer. Therefore 3 is also a rational,a real, and a complex. The same is true of the Scheme numbersthat model 3. For Scheme numbers, these types are defined by thepredicatesnumber?,complex?,real?,rational?,andinteger?.
There is no simple relationship between a number's type and itsrepresentation inside a computer. Although most implementations ofScheme will offer at least two different representations of 3, thesedifferent representations denote the same integer.
Scheme's numerical operations treat numbers as abstract data, asindependent of their representation as possible. Although an implementationof Scheme may use fixnum, flonum, and perhaps other representations fornumbers, this should not be apparent to a casual programmer writingsimple programs.
It is necessary, however, to distinguish between numbers that arerepresented exactly and those that may not be. For example, indexesinto data structures must be known exactly, as must some polynomialcoefficients in a symbolic algebra system. On the other hand, theresults of measurements are inherently inexact, and irrational numbersmay be approximated by rational and therefore inexact approximations.In order to catch uses of inexact numbers where exact numbers arerequired, Scheme explicitly distinguishes exact from inexact numbers.This distinction is orthogonal to the dimension of type.
Scheme numbers are eitherexact orinexact. A number isexact if it was written as an exact constant or was derived fromexact numbers using only exact operations. A number isinexact if it was written as an inexact constant,if it wasderived using inexact ingredients, or if it was derived usinginexact operations. Thus inexactness is a contagiousproperty of a number.If two implementations produce exact results for acomputation that did not involve inexact intermediate results,the two ultimate results will be mathematically equivalent. This isgenerally not true of computations involving inexact numberssince approximate methods such as floating point arithmetic may be used,but it is the duty of each implementation to make the result as close aspractical to the mathematically ideal result.
Rational operations such as+ should always produceexact results when given exact arguments.If the operation is unable to produce an exact result,then it may either report the violation of an implementation restrictionor it may silently coerce itsresult to an inexact value.See section 6.2.3.
With the exception ofinexact->exact, the operations described inthis section must generally return inexact results when given any inexactarguments. An operation may, however, return an exact result if it canprove that the value of the result is unaffected by the inexactness of itsarguments. For example, multiplication of any number by an exact zeromay produce an exact zero result, even if the other argument isinexact.
Implementations of Scheme are not required to implement the wholetower of subtypes given in section 6.2.1,but they must implement a coherent subset consistent with both thepurposes of the implementation and the spirit of the Scheme language.For example, an implementation in which all numbers are realmay still be quite useful.
Implementations may also support only a limited range of numbers ofany type, subject to the requirements of this section. The supportedrange for exact numbers of any type may be different from thesupported range for inexact numbers of that type. For example,an implementation that uses flonums to represent all itsinexact real numbers maysupport a practically unbounded range of exact integersand rationalswhile limiting the range of inexact reals (and thereforethe range of inexact integers and rationals)to the dynamic range of the flonum format.Furthermorethe gaps between the representable inexact integers andrationals arelikely to be very large in such an implementation as the limits of thisrange are approached.
An implementation of Scheme must support exact integersthroughout the range of numbers that may be used for indexes oflists, vectors, and strings or that may result from computing the length of alist, vector, or string. Thelength,vector-length,andstring-length procedures must return an exactinteger, and it is an error to use anything but an exact integer as anindex. Furthermore any integer constant within the index range, ifexpressed by an exact integer syntax, will indeed be read as an exactinteger, regardless of any implementation restrictions that may applyoutside this range. Finally, the procedures listed below will alwaysreturn an exact integer result provided all their arguments are exact integersand the mathematically expected result is representable as an exact integerwithin the implementation:
+ - *
quotient remainder modulo
max min abs
numerator denominator gcd
lcm floor ceiling
truncate round rationalize
expt
Implementations are encouraged, but not required, to supportexact integers and exact rationals ofpractically unlimited size and precision, and to implement theabove procedures and the/ procedure insuch a way that they always return exact results when given exactarguments. If one of these procedures is unable to deliver an exactresult when given exact arguments, then it may either report aviolation of animplementation restriction or it may silently coerce its result to aninexact number. Such a coercion may cause an error later.
An implementation may use floating point and other approximate representation strategies for inexact numbers.This report recommends, but does not require, that the IEEE 32-bitand 64-bit floating point standards be followed by implementations that useflonum representations, and that implementations usingother representations should match or exceed the precision achievableusing these floating point standards [12].
In particular, implementations that use flonum representationsmust follow these rules: A flonum resultmust be represented with at least as much precision as is used to express any ofthe inexact arguments to that operation. It is desirable (but not required) forpotentially inexact operations such assqrt, when applied to exactarguments, to produce exact answers whenever possible (for example thesquare root of an exact 4 ought to be an exact 2).If, however, anexact number is operated upon so as to produce an inexact result(as bysqrt), and if the result is represented as a flonum, thenthe most precise flonum format available must be used; but if the resultis represented in some other way then the representation must have at least asmuch precision as the most precise flonum format available.
Although Scheme allows a variety of writtennotations fornumbers, any particular implementation may support only some of them.For example, an implementation in which all numbers are realneed not support the rectangular and polar notations for complexnumbers. If an implementation encounters an exact numerical constant thatit cannot represent as an exact number, then it may either report aviolation of an implementation restriction or it may silently represent theconstant by an inexact number.
The syntax of the written representations for numbers is described formally insection 7.1.1. Note that case is not significant in numericalconstants.
A number may be written in binary, octal, decimal, orhexadecimal by the use of a radix prefix. The radix prefixes are#b (binary),#o (octal),#d (decimal), and#x (hexadecimal). Withno radix prefix, a number is assumed to be expressed in decimal.
Anumerical constant may be specified to be either exact orinexact by a prefix. The prefixes are#efor exact, and#i for inexact. An exactnessprefix may appear before or after any radix prefix that is used. Ifthe written representation of a number has no exactness prefix, theconstant may be either inexact or exact. It isinexact if it contains a decimal point, anexponent, or a ``#'' character in the place of a digit,otherwise it is exact.In systems with inexact numbersof varying precisions it may be useful to specifythe precision of a constant. For this purpose, numerical constantsmay be written with an exponent marker that indicates thedesired precision of the inexactrepresentation. The letterss,f,d, andl specify the use ofshort,single,double, andlong precision, respectively. (When fewerthan four internalinexactrepresentations exist, the four sizespecifications are mapped onto those available. For example, animplementation with two internal representations may map short andsingle together and long and double together.) In addition, theexponent markere specifies the default precision for theimplementation. The default precision has at least as much precisionasdouble, butimplementations may wish to allow this default to be set by the user.
3.14159265358979F0
Round to single --- 3.141593
0.6L0
Extend to long --- .600000000000000
The reader is referred to section 1.3.3 for a summaryof the naming conventions used to specify restrictions on the types ofarguments to numerical routines.The examples used in this section assume that any numerical constant writtenusing an exact notation is indeed represented as an exactnumber. Some examples also assume that certain numerical constants writtenusing an inexact notation can be represented without loss ofaccuracy; the inexact constants were chosen so that this islikely to be true in implementations that use flonums to representinexact numbers.
These numerical type predicates can be applied to any kind ofargument, including non-numbers. They return#t if the object isof the named type, and otherwise they return#f.In general, if a type predicate is true of a number then all highertype predicates are also true of that number. Consequently, if a typepredicate is false of a number, then all lower type predicates arealso false of that number.Ifz is an inexact complex number, then(real?z) is true ifand only if(zero? (imag-partz)) is true. Ifx is an inexactreal number, then(integer?x) is true if and only if(=x (roundx)).
(complex? 3+4i) ===> #t
(complex? 3) ===> #t
(real? 3) ===> #t
(real? -2.5+0.0i) ===> #t
(real? #e1e10) ===> #t
(rational? 6/10) ===> #t
(rational? 6/3) ===> #t
(integer? 3+0i) ===> #t
(integer? 3.0) ===> #t
(integer? 8/4) ===> #t
Note: The behavior of these type predicates on inexact numbersis unreliable, since any inaccuracy may affect the result.
Note: In many implementations therational? procedure will be the sameasreal?, and thecomplex? procedure will be the same asnumber?, but unusual implementations may be able to representsome irrational numbers exactly or may extend the number system tosupport some kind of non-complex numbers.
These numerical predicates provide tests for the exactness of aquantity. For any Scheme number, precisely one of these predicatesis true.
These procedures return#t if their arguments are (respectively):equal, monotonically increasing, monotonically decreasing,monotonically nondecreasing, or monotonically nonincreasing.
These predicates are required to be transitive.
Note: The traditional implementations of these predicates in Lisp-likelanguages are not transitive.
Note: While it is not an error to compare inexact numbers using thesepredicates, the results may be unreliable because a small inaccuracymay affect the result; this is especially true of= andzero?.When in doubt, consult a numerical analyst.
These numerical predicates test a number for a particular property,returning#t or#f. See note above.
These procedures return the maximum or minimum of their arguments.
(max 3 4) ===> 4 ; exact
(max 3.9 4) ===> 4.0 ; inexact
Note: If any argument is inexact, then the result will also be inexact (unlessthe procedure can prove that the inaccuracy is not large enough to affect theresult, which is possible only in unusual implementations). Ifmin ormax is used to compare numbers of mixed exactness, and the numericalvalue of the result cannot be represented as an inexact number without loss ofaccuracy, then the procedure may report a violation of an implementationrestriction.
These procedures return the sum or product of their arguments.
(+ 3 4) ===> 7
(+ 3) ===> 3
(+) ===> 0
(* 4) ===> 4
(*) ===> 1
With two or more arguments, these procedures return the difference orquotient of their arguments, associating to the left. With one argument,however, they return the additive or multiplicative inverse of their argument.
(- 3 4) ===> -1
(- 3 4 5) ===> -6
(- 3) ===> -3
(/ 3 4 5) ===> 3/20
(/ 3) ===> 1/3
Abs returns the absolute value of its argument.
(abs -7) ===> 7
These procedures implement number-theoretic (integer)division.n2 should be non-zero. All three proceduresreturn integers. Ifn1/n2 is an integer:
(quotient n1 n2) ===> n1/n2
(remainder n1 n2) ===> 0
(modulo n1 n2) ===> 0
Ifn1/n2 is not an integer:
(quotient n1 n2) ===> nq
(remainder n1 n2) ===> nr
(modulo n1 n2) ===> nm
wherenq isn1/n2 rounded towards zero,0 < |nr| < |n2|, 0 < |nm| < |n2|,nr andnm differ fromn1 by a multiple ofn2,nr has the same sign asn1, andnm has the same sign asn2.
From this we can conclude that for integersn1 andn2 withn2 not equal to 0,
(= n1 (+ (* n2 (quotient n1 n2))
(remainder n1 n2)))
===> #t
provided all numbers involved in that computation are exact.
(modulo 13 4) ===> 1
(remainder 13 4) ===> 1
(modulo -13 4) ===> 3
(remainder -13 4) ===> -1
(modulo 13 -4) ===> -3
(remainder 13 -4) ===> 1
(modulo -13 -4) ===> -1
(remainder -13 -4) ===> -1
(remainder -13 -4.0) ===> -1.0 ; inexact
These procedures return the greatest common divisor or least commonmultiple of their arguments. The result is always non-negative.
(gcd 32 -36) ===> 4
(gcd) ===> 0
(lcm 32 -36) ===> 288
(lcm 32.0 -36) ===> 288.0 ; inexact
(lcm) ===> 1
These procedures return the numerator or denominator of theirargument; the result is computed as if the argument was represented asa fraction in lowest terms. The denominator is always positive. Thedenominator of 0 is defined to be 1.
(numerator (/ 6 4)) ===> 3
(denominator (/ 6 4)) ===> 2
(denominator
(exact->inexact (/ 6 4))) ===> 2.0
These procedures return integers.Floor returns the largest integer not larger thanx.Ceiling returns the smallest integer not smaller than x.Truncate returns the integer closest tox whose absolutevalue is not larger than the absolute value ofx.Round returns theclosest integer tox, rounding to even whenx is halfway between twointegers.
Rationale: Round rounds to even for consistency with the default roundingmode specified by the IEEE floating point standard.
Note: If the argument to one of these procedures is inexact, then the resultwill also be inexact. If an exact value is needed, theresult should be passed to theinexact->exact procedure.
(floor -4.3) ===> -5.0
(ceiling -4.3) ===> -4.0
(truncate -4.3) ===> -4.0
(round -4.3) ===> -4.0
(floor 3.5) ===> 3.0
(ceiling 3.5) ===> 4.0
(truncate 3.5) ===> 3.0
(round 3.5) ===> 4.0 ; inexact
(round 7/2) ===> 4 ; exact
(round 7) ===> 7
Rationalize returns thesimplest rational numberdiffering fromx by no more thany. A rational numberr1 issimpler than another rational numberr2 ifr1 =p1/q1 andr2 =p2/q2 (in lowest terms) and |p1|< |p2| and |q1|< |q2|. Thus 3/5 is simpler than 4/7.Although not all rationals are comparable in this ordering (consider 2/7and 3/5) any interval contains a rational number that is simpler thanevery other rational number in that interval (the simpler 2/5 liesbetween 2/7 and 3/5). Note that 0 = 0/1 is the simplest rational ofall.
(rationalize
(inexact->exact .3) 1/10) ===> 1/3 ; exact
(rationalize .3 1/10) ===> #i1/3 ; inexact
These procedures are part of every implementation that supportsgeneralreal numbers; they compute the usual transcendental functions.Logcomputes the natural logarithm ofz (not the base ten logarithm).Asin,acos, andatan compute arcsine (sin-1),arccosine (cos-1), and arctangent (tan-1), respectively.The two-argument variant ofatan computes(angle(make-rectangularxy)) (see below), even in implementationsthat don't support general complex numbers.
In general, the mathematical functions log, arcsine, arccosine, andarctangent are multiply defined.The value oflogz is defined to be the one whose imaginarypart lies in the range from -
(exclusive) to
(inclusive).log 0 is undefined.Withlog defined this way, the values ofsin-1z,cos-1z,andtan-1z are according to the following formulæ:
| sin-1z = -ilog (iz + (1 -z2)1/2) |
cos-1z = / 2 -sin-1z |
| tan-1z = (log (1 +iz) -log (1 -iz)) / (2i) |
The above specification follows [27], which in turncites [19]; refer to these sources for more detaileddiscussion of branch cuts, boundary conditions, and implementation ofthese functions. When it is possible these procedures produce a realresult from a real argument.
Returns the principal square root ofz. The result will haveeither positive real part, or zero real part and non-negative imaginarypart.
Returnsz1 raised to the powerz2. Forz1
0
| z1z2 =ez2logz1 |
0z is 1 ifz = 0 and 0 otherwise.
These procedures are part of every implementation that supportsgeneralcomplex numbers. Supposex1,x2,x3, andx4 arereal numbers andz is a complex number such that
| z =x1 +x2i =x3 ·eix4 |
Then
(make-rectangular x1 x2) ===> z
(make-polar x3 x4) ===> z
(real-part z) ===> x1
(imag-part z) ===> x2
(magnitude z) ===> |x3|
(angle z) ===> xangle
where -
<xangle<
withxangle =x4 + 2
nfor some integern.
Rationale: Magnitude is the same asabs for a real argument,butabs must be present in all implementations, whereasmagnitude need only be present in implementations that supportgeneral complex numbers.
Exact->inexact returns an inexact representation ofz.The value returned is theinexact number that is numerically closest to the argument. If an exact argument has no reasonably close inexact equivalent,then a violation of an implementation restriction may be reported.
Inexact->exact returns an exact representation ofz. The value returned is the exact number that is numericallyclosest to the argument.If an inexact argument has no reasonably close exact equivalent,then a violation of an implementation restriction may be reported.
These procedures implement the natural one-to-one correspondence betweenexact and inexact integers throughout animplementation-dependent range. See section 6.2.3.
Radix must be an exact integer, either 2, 8, 10, or 16. If omitted,radix defaults to 10.The procedurenumber->string takes anumber and a radix and returns as a string an external representation ofthe given number in the given radix such that
(let ((number number)
(radix radix))
(eqv? number
(string->number (number->string number
radix)
radix)))
is true. It is an error if no possible result makes this expression true.
Ifz is inexact, the radix is 10, and the above expressioncan be satisfied by a result that contains a decimal point,then the result contains a decimal point and is expressed using theminimum number of digits (exclusive of exponent and trailingzeroes) needed to make the above expressiontrue [3, 5];otherwise the format of the result is unspecified.
The result returned bynumber->stringnever contains an explicit radix prefix.
Note: The error case can occur only whenz is not a complex numberor is a complex number with a non-rational real or imaginary part.
Rationale: Ifz is an inexact number represented using flonums, andthe radix is 10, then the above expression is normally satisfied bya result containing a decimal point. The unspecified caseallows for infinities, NaNs, and non-flonum representations.
Returns a number of the maximally precise representation expressed by thegivenstring.Radix must be an exact integer, either 2, 8, 10,or 16. If supplied,radix is a default radix that may be overriddenby an explicit radix prefix instring (e.g."#o177"). Ifradixis not supplied, then the default radix is 10. Ifstring is nota syntactically valid notation for a number, thenstring->numberreturns#f.
(string->number "100") ===> 100
(string->number "100" 16) ===> 256
(string->number "1e2") ===> 100.0
(string->number "15##") ===> 1500.0
Note: The domain ofstring->number may be restricted by implementationsin the following ways.String->number is permitted to return#f wheneverstring contains an explicit radix prefix.If all numbers supported by an implementation are real, thenstring->number is permitted to return#f wheneverstring uses the polar or rectangular notations for complexnumbers. If all numbers are integers, thenstring->number may return#f wheneverthe fractional notation is used. If all numbers are exact, thenstring->number may return#f wheneveran exponent marker or explicit exactness prefix is used, or ifa# appears in place of a digit. If all inexactnumbers are integers, thenstring->number may return#f whenevera decimal point is used.
This section describes operations on some of Scheme's non-numeric data types:booleans, pairs, lists, symbols, characters, strings and vectors.
The standard boolean objects for true and false are written as#t and#f. What reallymatters, though, are the objects that the Scheme conditional expressions(if,cond,and,or,do) treat astrue or false. The phrase ``a true value''(or sometimes just ``true'') means any object treated as true by theconditional expressions, and the phrase ``a false value'' (or``false'') means any object treated as false by the conditional expressions.
Of all the standard Scheme values, only#fcounts as false in conditional expressions.Except for#f,all standard Scheme values, including#t,pairs, the empty list, symbols, numbers, strings, vectors, and procedures,count as true.
Note: Programmers accustomed to other dialects of Lisp should be aware thatScheme distinguishes both#f and the empty listfrom the symbolnil.
Boolean constants evaluate to themselves, so they do not need to be quotedin programs.
#t ===> #t
#f ===> #f
'#f ===> #f
Not returns#t ifobj is false, and returns#f otherwise.
(not #t) ===> #f
(not 3) ===> #f
(not (list 3)) ===> #f
(not #f) ===> #t
(not '()) ===> #f
(not (list)) ===> #f
(not 'nil) ===> #f
Boolean? returns#t ifobj is either#t or#f and returns#f otherwise.
(boolean? #f) ===> #t
(boolean? 0) ===> #f
(boolean? '()) ===> #f
Apair (sometimes called adotted pair) is arecord structure with two fields called the car and cdr fields (forhistorical reasons). Pairs are created by the procedurecons.The car and cdr fields are accessed by the procedurescar andcdr. The car and cdr fields are assigned by the proceduresset-car! andset-cdr!.
Pairs are used primarily to represent lists. A list canbe defined recursively as either the empty list or a pair whosecdr is a list. More precisely, the set of lists is defined as the smallestsetX such that
The objects in the car fields of successive pairs of a list are theelements of the list. For example, a two-element list is a pair whose caris the first element and whose cdr is a pair whose car is the second elementand whose cdr is the empty list. The length of a list is the number ofelements, which is the same as the number of pairs.
The empty list is a special object of its own type(it is not a pair); it has no elements and its length is zero.
Note: The above definitions imply that all lists have finite length and areterminated by the empty list.
The most general notation (external representation) for Scheme pairs isthe ``dotted'' notation(c1 .c2) wherec1 is the value of the car field andc2 is the value of thecdr field. For example(4 . 5) is a pair whose car is 4 and whosecdr is 5. Note that(4 . 5) is the external representation of apair, not an expression that evaluates to a pair.
A more streamlined notation can be used for lists: the elements of thelist are simply enclosed in parentheses and separated by spaces. Theempty list is written() . For example,
(a b c d e)
and
(a . (b . (c . (d . (e . ())))))
are equivalent notations for a list of symbols.
A chain of pairs not ending in the empty list is called animproper list. Note that an improper list is not a list.The list and dotted notations can be combined to representimproper lists:
(a b c . d)
is equivalent to
(a . (b . (c . d)))
Whether a given pair is a list depends upon what is stored in the cdrfield. When theset-cdr! procedure is used, an object can be alist one moment and not the next:
(define x (list 'a 'b 'c))
(define y x)
y ===> (a b c)
(list? y) ===> #t
(set-cdr! x 4) ===> unspecified
x ===> (a . 4)
(eqv? x y) ===> #t
y ===> (a . 4)
(list? y) ===> #f
(set-cdr! x x) ===> unspecified
(list? x) ===> #f
Within literal expressions and representations of objects read by theread procedure, the forms'<datum>,`<datum>,,<datum>, and,@<datum> denote two-element lists whose first elements arethe symbolsquote,quasiquote,unquote, andunquote-splicing, respectively. The second element in each caseis <datum>. This convention is supported so that arbitrary Schemeprograms may be represented as lists. That is, according to Scheme's grammar, every<expression> is also a <datum> (see section 7.1.2).Among other things, this permits the use of theread procedure toparse Scheme programs. See section 3.3.
Pair? returns#t ifobj is a pair, and otherwisereturns#f.
(pair? '(a . b)) ===> #t
(pair? '(a b c)) ===> #t
(pair? '()) ===> #f
(pair? '#(a b)) ===> #f
Returns a newly allocated pair whose car isobj1 and whose cdr isobj2. The pair is guaranteed to be different (in the sense ofeqv?) from every existing object.
(cons 'a '()) ===> (a)
(cons '(a) '(b c d)) ===> ((a) b c d)
(cons "a" '(b c)) ===> ("a" b c)
(cons 'a 3) ===> (a . 3)
(cons '(a b) 'c) ===> ((a b) . c)
Returns the contents of the car field ofpair. Note that it is anerror to take the car of the empty list.
(car '(a b c)) ===> a
(car '((a) b c d)) ===> (a)
(car '(1 . 2)) ===> 1
(car '()) ===> error
Returns the contents of the cdr field ofpair.Note that it is an error to take the cdr of the empty list.
(cdr '((a) b c d)) ===> (b c d)
(cdr '(1 . 2)) ===> 2
(cdr '()) ===> error
Storesobj in the car field ofpair.The value returned byset-car! is unspecified.
(define (f) (list 'not-a-constant-list))
(define (g) '(constant-list))
(set-car! (f) 3) ===> unspecified
(set-car! (g) 3) ===> error
Storesobj in the cdr field ofpair.The value returned byset-cdr! is unspecified.
:
These procedures are compositions ofcar andcdr, wherefor examplecaddr could be defined by
(define caddr (lambda (x) (car (cdr (cdr x))))).
Arbitrary compositions, up to four deep, are provided. There aretwenty-eight of these procedures in all.
Returns#t ifobj is the empty list,otherwise returns#f.
Returns#t ifobj is a list, otherwise returns#f.By definition, all lists have finite length and are terminated bythe empty list.
(list? '(a b c)) ===> #t
(list? '()) ===> #t
(list? '(a . b)) ===> #f
(let ((x (list 'a)))
(set-cdr! x x)
(list? x)) ===> #f
Returns a newly allocated list of its arguments.
(list 'a (+ 3 4) 'c) ===> (a 7 c)
(list) ===> ()
Returns the length oflist.
(length '(a b c)) ===> 3
(length '(a (b) (c d e))) ===> 3
(length '()) ===> 0
Returns a list consisting of the elements of the firstlistfollowed by the elements of the otherlists.
(append '(x) '(y)) ===> (x y)
(append '(a) '(b c d)) ===> (a b c d)
(append '(a (b)) '((c))) ===> (a (b) (c))
The resulting list is always newly allocated, except that it sharesstructure with the lastlist argument. The last argument mayactually be any object; an improper list results if the last argument is not aproper list.
(append '(a b) '(c . d)) ===> (a b c . d)
(append '() 'a) ===> a
Returns a newly allocated list consisting of the elements oflistin reverse order.
(reverse '(a b c)) ===> (c b a)
(reverse '(a (b c) d (e (f))))
===> ((e (f)) d (b c) a)
Returns the sublist oflist obtained by omitting the firstkelements. It is an error iflist has fewer thank elements.List-tail could be defined by
(define list-tail
(lambda (x k)
(if (zero? k)
x
(list-tail (cdr x) (- k 1)))))
Returns thekth element oflist. (This is the sameas the car of(list-taillistk).)It is an error iflist has fewer thank elements.
(list-ref '(a b c d) 2) ===> c
(list-ref '(a b c d)
(inexact->exact (round 1.8)))
===> c
These procedures return the first sublist oflist whose car isobj, where the sublists oflist are the non-empty listsreturned by(list-taillistk) fork lessthan the length oflist. Ifobj does not occur inlist, then#f (not the empty list) isreturned.Memq useseq? to compareobj with the elements oflist, whilememv useseqv? andmember usesequal?.
(memq 'a '(a b c)) ===> (a b c)
(memq 'b '(a b c)) ===> (b c)
(memq 'a '(b c d)) ===> #f
(memq (list 'a) '(b (a) c)) ===> #f
(member (list 'a)
'(b (a) c)) ===> ((a) c)
(memq 101 '(100 101 102)) ===> unspecified
(memv 101 '(100 101 102)) ===> (101 102)
Alist (for ``association list'') must be a list ofpairs. These procedures find the first pair inalist whose car field isobj,and returns that pair. If no pair inalist hasobj as itscar, then#f (not the empty list) is returned.Assq useseq? to compareobj with the car fields of the pairs inalist,whileassv useseqv? andassoc usesequal?.
(define e '((a 1) (b 2) (c 3)))
(assq 'a e) ===> (a 1)
(assq 'b e) ===> (b 2)
(assq 'd e) ===> #f
(assq (list 'a) '(((a)) ((b)) ((c))))
===> #f
(assoc (list 'a) '(((a)) ((b)) ((c))))
===> ((a))
(assq 5 '((2 3) (5 7) (11 13)))
===> unspecified
(assv 5 '((2 3) (5 7) (11 13)))
===> (5 7)
Rationale: Although they are ordinarily used as predicates,memq,memv,member,assq,assv, andassoc do nothave question marks in their names because they return useful values ratherthan just#t or#f.
Symbols are objects whose usefulness rests on the fact that twosymbols are identical (in the sense ofeqv?) if and only if theirnames are spelled the same way. This is exactly the property needed torepresent identifiers in programs, and so mostimplementations of Scheme use them internally for that purpose. Symbolsare useful for many other applications; for instance, they may be usedthe way enumerated values are used in Pascal.
The rules for writing a symbol are exactly the same as the rules forwriting an identifier; see sections 2.1and 7.1.1.
It is guaranteed that any symbol that has been returned as part ofa literal expression, or read using theread procedure, andsubsequently written out using thewrite procedure, will read backin as the identical symbol (in the sense ofeqv?). Thestring->symbol procedure, however, can create symbols forwhich this write/read invariance may not hold because their namescontain special characters or letters in the non-standard case.
Note: Some implementations of Scheme have a feature known as ``slashification''in order to guarantee write/read invariance for all symbols, buthistorically the most important use of this feature has been tocompensate for the lack of a string data type.Some implementations also have ``uninterned symbols'', whichdefeat write/read invariance even in implementations with slashification,and also generate exceptions to the rule that two symbols are the sameif and only if their names are spelled the same.
Returns#t ifobj is a symbol, otherwise returns#f.
(symbol? 'foo) ===> #t
(symbol? (car '(a b))) ===> #t
(symbol? "bar") ===> #f
(symbol? 'nil) ===> #t
(symbol? '()) ===> #f
(symbol? #f) ===> #f
Returns the name ofsymbol as a string. If the symbol was part ofan object returned as the value of a literal expression(section 4.1.2) or by a call to theread procedure,and its name contains alphabetic characters, then the string returnedwill contain characters in the implementation's preferred standardcase -- some implementations will prefer upper case, others lower case.If the symbol was returned bystring->symbol, the case ofcharacters in the string returned will be the same as the case in thestring that was passed tostring->symbol. It is an errorto apply mutation procedures likestring-set! to strings returnedby this procedure.
The following examples assume that the implementation's standard case islower case:
(symbol->string 'flying-fish)
===> "flying-fish"
(symbol->string 'Martin) ===> "martin"
(symbol->string
(string->symbol "Malvina"))
===> "Malvina"
Returns the symbol whose name isstring. This procedure cancreate symbols with names containing special characters or letters inthe non-standard case, but it is usually a bad idea to create suchsymbols because in some implementations of Scheme they cannot be read asthemselves. Seesymbol->string.
The following examples assume that the implementation's standard case islower case:
(eq? 'mISSISSIppi 'mississippi)
===> #t
(string->symbol "mISSISSIppi")
===> the symbol with name "mISSISSIppi"
(eq? 'bitBlt (string->symbol "bitBlt"))
===> #f
(eq? 'JollyWog
(string->symbol
(symbol->string 'JollyWog)))
===> #t
(string=? "K. Harper, M.D."
(symbol->string
(string->symbol "K. Harper, M.D.")))
===> #t
Characters are objects that represent printed characters such asletters and digits. Characters are written using the notation#\<character>or#\<character name>.For example:
|
Case is significant in#\<character>, but not in#\<character name>. If <character> in#\<character> is alphabetic, then the characterfollowing <character> must be a delimiter character such as aspace or parenthesis. This rule resolves the ambiguous case where, forexample, the sequence of characters ``#\space''could be taken to be either a representation of the space character or arepresentation of the character ``#\s'' followedby a representation of the symbol ``pace.''
Characters written in the#\ notation are self-evaluating.That is, they do not have to be quoted in programs. Some of the procedures that operate on characters ignore thedifference between upper case and lower case. The procedures thatignore case have ``-ci'' (for ``caseinsensitive'') embedded in their names.
Returns#t ifobj is a character, otherwise returns#f.
These procedures impose a total ordering on the set of characters. Itis guaranteed that under this ordering:
Some implementations may generalize these procedures to take more thantwo arguments, as with the corresponding numerical predicates.
These procedures are similar tochar=? et cetera, but they treatupper case and lower case letters as the same. For example,(char-ci=? #\A #\a) returns#t. Someimplementations may generalize these procedures to take more than twoarguments, as with the corresponding numerical predicates.
These procedures return#t if their arguments are alphabetic,numeric, whitespace, upper case, or lower case characters, respectively,otherwise they return#f. The following remarks, which are specific tothe ASCII character set, are intended only as a guide: The alphabetic charactersare the 52 upper and lower case letters. The numeric characters are theten decimal digits. The whitespace characters are space, tab, linefeed, form feed, and carriage return.
Given a character,char->integer returns an exact integerrepresentation of the character. Given an exact integer that is the image ofa character underchar->integer,integer->charreturns that character. These procedures implement order-preserving isomorphismsbetween the set of characters under thechar<=? ordering and somesubset of the integers under the<= ordering. That is, if
(char<=? a b) ===> #t and (<= x y) ===> #t
andx andy are in the domain ofinteger->char, then
(<= (char->integer a)
(char->integer b)) ===> #t
(char<=? (integer->char x)
(integer->char y)) ===> #t
These procedures return a characterchar2 such that(char-ci=?charchar2). In addition, ifchar isalphabetic, then the result ofchar-upcase is upper case and theresult ofchar-downcase is lower case.
Strings are sequences of characters. Strings are written as sequences of characters enclosed within doublequotes("). A doublequote can be written inside a string only by escapingit with a backslash (\), as in
"The word \"recursion\" has many meanings."
A backslash can be written inside a string only by escaping it with anotherbackslash. Scheme does not specify the effect of a backslash within astring that is not followed by a doublequote or backslash.
A string constant may continue from one line to the next, butthe exact contents of such a string are unspecified.Thelength of a string is the number of characters that itcontains. This number is an exact, non-negative integer that is fixed when thestring is created. Thevalid indexes of a string are theexact non-negative integers less than the length of the string. The firstcharacter of a string has index 0, the second has index 1, and so on.
In phrases such as ``the characters ofstring beginning withindexstart and ending with indexend,'' it is understoodthat the indexstart is inclusive and the indexend isexclusive. Thus ifstart andend are the same index, a nullsubstring is referred to, and ifstart is zero andend isthe length ofstring, then the entire string is referred to.
Some of the procedures that operate on strings ignore thedifference between upper and lower case. The versions that ignore casehave ``-ci'' (for ``case insensitive'') embedded in theirnames.
Returns#t ifobj is a string, otherwise returns#f.
Make-string returns a newly allocated string oflengthk. Ifchar is given, then all elements of the stringare initialized tochar, otherwise the contents of thestring are unspecified.
Returns a newly allocated string composed of the arguments.
Returns the number of characters in the givenstring.
k must be a valid index ofstring.String-ref returns characterk ofstring using zero-origin indexing.
k must be a valid index ofstring.String-set! storeschar in elementk ofstringand returns an unspecified value.
(define (f) (make-string 3 #\*))
(define (g) "***")
(string-set! (f) 0 #\?) ===> unspecified
(string-set! (g) 0 #\?) ===> error
(string-set! (symbol->string 'immutable)
0
#\?) ===> error
Returns#t if the two strings are the same length and contain the samecharacters in the same positions, otherwise returns#f.String-ci=? treatsupper and lower case letters as though they were the same character, butstring=? treats upper and lower case as distinct characters.
These procedures are the lexicographic extensions to strings of thecorresponding orderings on characters. For example,string<? isthe lexicographic ordering on strings induced by the orderingchar<? on characters. If two strings differ in length butare the same up to the length of the shorter string, the shorter stringis considered to be lexicographically less than the longer string.
Implementations may generalize these and thestring=? andstring-ci=? procedures to take more than two arguments, as withthe corresponding numerical predicates.
String must be a string, andstart andendmust be exact integers satisfying
| 0<start<end<(string-lengthstring). |
Substring returns a newly allocated string formed from the characters ofstring beginning with indexstart (inclusive) and ending with indexend (exclusive).
Returns a newly allocated string whose characters form the concatenation of thegiven strings.
String->list returns a newly allocated list of thecharacters that make up the given string.List->stringreturns a newly allocated string formed from the characters in the listlist, which must be a list of characters.String->listandlist->string areinverses so far asequal? is concerned.
Returns a newly allocated copy of the givenstring.
Storeschar in every element of the givenstring and returns anunspecified value.
Vectors are heterogenous structures whose elements are indexedby integers. A vector typically occupies less space than a listof the same length, and the average time required to access a randomlychosen element is typically less for the vector than for the list.
Thelength of a vector is the number of elements that itcontains. This number is a non-negative integer that is fixed when thevector is created. Thevalid indexes of avector are the exact non-negative integers less than the length of thevector. The first element in a vector is indexed by zero, and the lastelement is indexed by one less than the length of the vector.
Vectors are written using the notation#(obj...).For example, a vector of length 3 containing the number zero in element0, the list(2 2 2 2) in element 1, and the string"Anna" inelement 2 can be written as following:
#(0 (2 2 2 2) "Anna")
Note that this is the external representation of a vector, not anexpression evaluating to a vector. Like list constants, vectorconstants must be quoted:
'#(0 (2 2 2 2) "Anna")
===> #(0 (2 2 2 2) "Anna")
Returns#t ifobj is a vector, otherwise returns#f.
Returns a newly allocated vector ofk elements. If a secondargument is given, then each element is initialized tofill.Otherwise the initial contents of each element is unspecified.
Returns a newly allocated vector whose elements contain the givenarguments. Analogous tolist.
(vector 'a 'b 'c) ===> #(a b c)
Returns the number of elements invector as an exact integer.
k must be a valid index ofvector.Vector-ref returns the contents of elementk ofvector.
(vector-ref '#(1 1 2 3 5 8 13 21)
5)
===> 8
(vector-ref '#(1 1 2 3 5 8 13 21)
(let ((i (round (* 2 (acos -1)))))
(if (inexact? i)
(inexact->exact i)
i)))
===> 13
k must be a valid index ofvector.Vector-set! storesobj in elementk ofvector.The value returned byvector-set! is unspecified.
(let ((vec (vector 0 '(2 2 2 2) "Anna")))
(vector-set! vec 1 '("Sue" "Sue"))
vec)
===> #(0 ("Sue" "Sue") "Anna")
(vector-set! '#(0 1 2) 1 "doe")
===> error ; constant vector
Vector->list returns a newly allocated list of the objects containedin the elements ofvector.List->vector returns a newlycreated vector initialized to the elements of the listlist.
(vector->list '#(dah dah didah))
===> (dah dah didah)
(list->vector '(dididit dah))
===> #(dididit dah)
Storesfill in every element ofvector.The value returned byvector-fill! is unspecified.
This chapter describes various primitive procedures which control theflow of program execution in special ways.Theprocedure? predicate is also described here.
Returns#t ifobj is a procedure, otherwise returns#f.
(procedure? car) ===> #t
(procedure? 'car) ===> #f
(procedure? (lambda (x) (* x x)))
===> #t
(procedure? '(lambda (x) (* x x)))
===> #f
(call-with-current-continuation procedure?)
===> #t
Proc must be a procedure andargs must be a list.Callsproc with the elements of the list(append (listarg1...)args) as the actualarguments.
(apply + (list 3 4)) ===> 7
(define compose
(lambda (f g)
(lambda args
(f (apply g args)))))
((compose sqrt *) 12 75) ===> 30
Thelists must be lists, andproc must be aprocedure taking as many arguments as there arelistsand returning a single value. If morethan onelist is given, then they must all be the same length.Map appliesproc element-wise to the elements of thelists and returns a list of the results, in order.The dynamic order in whichproc is applied to the elements of thelists is unspecified.
(map cadr '((a b) (d e) (g h)))
===> (b e h)
(map (lambda (n) (expt n n))
'(1 2 3 4 5))
===> (1 4 27 256 3125)
(map + '(1 2 3) '(4 5 6)) ===> (5 7 9)
(let ((count 0))
(map (lambda (ignored)
(set! count (+ count 1))
count)
'(a b))) ===> (1 2) or (2 1)
The arguments tofor-each are like the arguments tomap, butfor-each callsproc for its side effects rather than for itsvalues. Unlikemap,for-each is guaranteed to callproc onthe elements of thelists in order from the first element(s) to thelast, and the value returned byfor-each is unspecified.
(let ((v (make-vector 5)))
(for-each (lambda (i)
(vector-set! v i (* i i)))
'(0 1 2 3 4))
v) ===> #(0 1 4 9 16)
Forces the value ofpromise (seedelay,section 4.2.5). If no value has been computed forthe promise, then a value is computed and returned. The value of thepromise is cached (or ``memoized'') so that if it is forced a secondtime, the previously computed value is returned.
(force (delay (+ 1 2))) ===> 3
(let ((p (delay (+ 1 2))))
(list (force p) (force p)))
===> (3 3)
(define a-stream
(letrec ((next
(lambda (n)
(cons n (delay (next (+ n 1)))))))
(next 0)))
(define head car)
(define tail
(lambda (stream) (force (cdr stream))))
(head (tail (tail a-stream)))
===> 2
Force anddelay are mainly intended for programs written infunctional style. The following examples should not be considered toillustrate good programming style, but they illustrate the property thatonly one value is computed for a promise, no matter how many times it isforced.
(define count 0)
(define p
(delay (begin (set! count (+ count 1))
(if (> count x)
count
(force p)))))
(define x 5)
p ===> a promise
(force p) ===> 6
p ===> a promise, still
(begin (set! x 10)
(force p)) ===> 6
Here is a possible implementation ofdelay andforce.Promises are implemented here as procedures of no arguments,andforce simply calls its argument:
(define force
(lambda (object)
(object)))
We define the expression
(delay <expression>)
to have the same meaning as the procedure call
(make-promise (lambda () <expression>))
as follows
(define-syntax delay
(syntax-rules ()
((delay expression)
(make-promise (lambda () expression))))),
wheremake-promise is defined as follows:
(define make-promise
(lambda (proc)
(let ((result-ready? #f)
(result #f))
(lambda ()
(if result-ready?
result
(let ((x (proc)))
(if result-ready?
result
(begin (set! result-ready? #t)
(set! result x)
result))))))))
Rationale: A promise may refer to its own value, as in the last example above.Forcing such a promise may cause the promise to be forced a second timebefore the value of the first force has been computed.This complicates the definition ofmake-promise.
Various extensions to this semantics ofdelay andforceare supported in some implementations:
(eqv? (delay 1) 1) ===> unspecified
(pair? (delay (cons 1 2))) ===> unspecified
(+ (delay (* 3 7)) 13) ===> 34
Proc must be a procedure of oneargument. The procedurecall-with-current-continuation packagesup the current continuation (see the rationale below) as an ``escapeprocedure'' and passes it as an argument toproc. The escape procedure is a Scheme procedure that, if it islater called, will abandon whatever continuation is in effect at that latertime and will instead use the continuation that was in effectwhen the escape procedure was created. Calling the escape proceduremay cause the invocation ofbefore andafter thunks installed usingdynamic-wind.
The escape procedure accepts the same number of arguments as the continuation tothe original call tocall-with-current-continuation.Except for continuations created by thecall-with-valuesprocedure, all continuations take exactly one value. Theeffect of passing no value or more than one value to continuationsthat were not created bycall-with-values is unspecified.
The escape procedure that is passed toproc hasunlimited extent just like any other procedure in Scheme. It may be storedin variables or data structures and may be called as many times as desired.
The following examples show only the most common ways in whichcall-with-current-continuation is used. If all real uses were assimple as these examples, there would be no need for a procedure withthe power ofcall-with-current-continuation.
(call-with-current-continuation
(lambda (exit)
(for-each (lambda (x)
(if (negative? x)
(exit x)))
'(54 0 37 -3 245 19))
#t)) ===> -3
(define list-length
(lambda (obj)
(call-with-current-continuation
(lambda (return)
(letrec ((r
(lambda (obj)
(cond ((null? obj) 0)
((pair? obj)
(+ (r (cdr obj)) 1))
(else (return #f))))))
(r obj))))))
(list-length '(1 2 3 4)) ===> 4
(list-length '(a b . c)) ===> #f
Rationale:A common use ofcall-with-current-continuation is forstructured, non-local exits from loops or procedure bodies, but in factcall-with-current-continuation is extremely useful for implementing awide variety of advanced control structures.
Whenever a Scheme expression is evaluated there is acontinuation wanting the result of the expression. The continuationrepresents an entire (default) future for the computation. If the expression isevaluated at top level, for example, then the continuation might take theresult, print it on the screen, prompt for the next input, evaluate it, andso on forever. Most of the time the continuation includes actionsspecified by user code, as in a continuation that will take the result,multiply it by the value stored in a local variable, add seven, and givethe answer to the top level continuation to be printed. Normally theseubiquitous continuations are hidden behind the scenes and programmers do notthink much about them. On rare occasions, however, a programmer mayneed to deal with continuations explicitly.Call-with-current-continuation allows Scheme programmers to dothat by creating a procedure that acts just like the currentcontinuation.
Most programming languages incorporate one or more special-purposeescape constructs with names likeexit,return, orevengoto. In 1965, however, Peter Landin [16]invented a general purpose escape operator called the J-operator. JohnReynolds [24] described a simpler but equally powerfulconstruct in 1972. Thecatch special form described by Sussmanand Steele in the 1975 report on Scheme is exactly the same asReynolds's construct, though its name came from a less general constructin MacLisp. Several Scheme implementors noticed that the full power of thecatch construct could be provided by a procedure instead of by aspecial syntactic construct, and the namecall-with-current-continuation was coined in 1982. This name isdescriptive, but opinions differ on the merits of such a long name, andsome people use the namecall/cc instead.
Delivers all of its arguments to its continuation.Except for continuations created by thecall-with-valuesprocedure, all continuations take exactly one value.Values might be defined as follows:
(define (values . things)
(call-with-current-continuation
(lambda (cont) (apply cont things))))
Calls itsproducer argument with no values anda continuation that, when passed some values, calls theconsumer procedure with those values as arguments.The continuation for the call toconsumer is thecontinuation of the call tocall-with-values.
(call-with-values (lambda () (values 4 5))
(lambda (a b) b))
===> 5
(call-with-values * -) ===> -1
Callsthunk without arguments, returning the result(s) of this call.Before andafter are called, also without arguments, as requiredby the following rules (note that in the absence of calls to continuationscaptured usingcall-with-current-continuation the three arguments arecalled once each, in order).Before is called whenever executionenters the dynamic extent of the call tothunk andafter is calledwhenever it exits that dynamic extent. The dynamic extent of a procedurecall is the period between when the call is initiated and when itreturns. In Scheme, because ofcall-with-current-continuation, thedynamic extent of a call may not be a single, connected time period.It is defined as follows:
If a second call todynamic-wind occurs within the dynamic extent of thecall tothunk and then a continuation is invoked in such a way that theafters from these two invocations ofdynamic-wind are both to becalled, then theafter associated with the second (inner) call todynamic-wind is called first.
If a second call todynamic-wind occurs within the dynamic extent of thecall tothunk and then a continuation is invoked in such a way that thebefores from these two invocations ofdynamic-wind are both to becalled, then thebefore associated with the first (outer) call todynamic-wind is called first.
If invoking a continuation requires calling thebefore from one calltodynamic-wind and theafter from another, then theafteris called first.
The effect of using a captured continuation to enter or exit the dynamicextent of a call tobefore orafter is undefined.
(let ((path '())
(c #f))
(let ((add (lambda (s)
(set! path (cons s path)))))
(dynamic-wind
(lambda () (add 'connect))
(lambda ()
(add (call-with-current-continuation
(lambda (c0)
(set! c c0)
'talk1))))
(lambda () (add 'disconnect)))
(if (< (length path) 4)
(c 'talk2)
(reverse path))))
===> (connect talk1 disconnect
connect talk2 disconnect)
Evaluatesexpression in the specified environment and returns its value.Expression must be a valid Scheme expression represented as data,andenvironment-specifier must be a value returned by one of thethree procedures described below.Implementations may extendeval to allow non-expression programs(definitions) as the first argument and to allow othervalues as environments, with the restriction thateval is notallowed to create new bindings in the environments associated withnull-environment orscheme-report-environment.
(eval '(* 7 3) (scheme-report-environment 5))
===> 21
(let ((f (eval '(lambda (f x) (f x x))
(null-environment 5))))
(f + 10))
===> 20
Version must be the exact integer5,corresponding to this revision of the Scheme report (theRevised5 Report on Scheme).Scheme-report-environment returns a specifier for anenvironment that is empty except for all bindings defined inthis report that are either required or both optional andsupported by the implementation.Null-environment returnsa specifier for an environment that is empty except for the(syntactic) bindings for all syntactic keywords defined inthis report that are either required or both optional andsupported by the implementation.
Other values ofversion can be used to specify environmentsmatching past revisions of this report, but their support is notrequired. An implementation will signal an error ifversionis neither5 nor another value supported bythe implementation.
The effect of assigning (through the use ofeval) a variablebound in ascheme-report-environment(for examplecar) is unspecified. Thus the environments specifiedbyscheme-report-environment may be immutable.
This procedure returns a specifier for the environment thatcontains implementation-defined bindings, typically a superset ofthose listed in the report. The intent is that this procedurewill return the environment in which the implementation would evaluateexpressions dynamically typed by the user.
Ports represent input and output devices. To Scheme, an input port is aScheme object that can deliver characters upon command, while an output portis a Scheme object that can accept characters.
String should be a string naming a file, andproc should be a procedure that accepts one argument.Forcall-with-input-file,the file should already exist; forcall-with-output-file,the effect is unspecified if the filealready exists. These procedures callproc with one argument: theport obtained by opening the named file for input or output. If thefile cannot be opened, an error is signalled. Ifproc returns,then the port is closed automatically and the value(s) yielded by theproc is(are) returned. Ifproc does not return, then the port will not be closed automatically unless it is possible toprove that the port will never again be used for a read or writeoperation.
Rationale: Because Scheme's escape procedures have unlimited extent, it ispossible to escape from the current continuation but later to escape back in. If implementations were permitted to close the port on any escape from thecurrent continuation, then it would be impossible to write portable code usingbothcall-with-current-continuation andcall-with-input-file orcall-with-output-file.
Returns#t ifobj is an input port or output portrespectively, otherwise returns#f.
Returns the current default input or output port.
String should be a string naming a file, andproc should be a procedure of no arguments.Forwith-input-from-file,the file should already exist; forwith-output-to-file,the effect is unspecified if the filealready exists.The file is opened for input or output, an input or output portconnected to it is made the default value returned bycurrent-input-port orcurrent-output-port(and is used by(read),(writeobj), and so forth),and thethunk is called with no arguments. When thethunk returns,the port is closed and the previous default is restored.With-input-from-file andwith-output-to-file return(s) thevalue(s) yielded bythunk.If an escape procedureis used to escape from the continuation of these procedures, theirbehavior is implementation dependent.
Takes a string naming an existing file and returns an input port capable ofdelivering characters from the file. If the file cannot be opened, an error issignalled.
Takes a string naming an output file to be created and returns an outputport capable of writing characters to a new file by that name. If the filecannot be opened, an error is signalled. If a file with the given namealready exists, the effect is unspecified.
Closes the file associated withport, rendering theportincapable of delivering or accepting characters. These routines have no effect if the file has already been closed.The value returned is unspecified.
Read converts external representations of Scheme objects into theobjects themselves. That is, it is a parser for the nonterminal<datum> (see sections 7.1.2 and6.3.2).Read returns the nextobject parsable from the given inputport, updatingport to point tothe first character past the end of the external representation of the object.
If an end of file is encountered in the input before anycharacters are found that can begin an object, then an end of fileobject is returned. The port remains open, and further attemptsto read will also return an end of file object. If an end of file isencountered after the beginning of an object's external representation,but the external representation is incomplete and therefore not parsable,an error is signalled.
Theport argument may be omitted, in which case it defaults to thevalue returned bycurrent-input-port. It is an error to read froma closed port.
Returns the next character available from the inputport, updatingtheport to point to the following character. If no more charactersare available, an end of file object is returned.Port may beomitted, in which case it defaults to the value returned bycurrent-input-port.
Returns the next character available from the inputport,without updatingtheport to point to the following character. If no more charactersare available, an end of file object is returned.Port may beomitted, in which case it defaults to the value returned bycurrent-input-port.
Note: The value returned by a call topeek-char is the same as thevalue that would have been returned by a call toread-char with thesameport. The only difference is that the very next call toread-char orpeek-char on thatport will return thevalue returned by the preceding call topeek-char. In particular, a calltopeek-char on an interactive port will hang waiting for inputwhenever a call toread-char would have hung.
Returns#t ifobj is an end of file object, otherwise returns#f. The precise set of end of file objects will vary amongimplementations, but in any case no end of file object will ever be an objectthat can be read in usingread.
Returns#t if a character is ready on the inputport andreturns#f otherwise. Ifchar-ready returns#t thenthe nextread-char operation on the givenport is guaranteednot to hang. If theport is at end of file thenchar-ready?returns#t.Port may be omitted, in which case it defaults tothe value returned bycurrent-input-port.
Rationale: Char-ready? exists to make it possible for a program toaccept characters from interactive ports without getting stuck waiting forinput. Any input editors associated with such ports must ensure thatcharacters whose existence has been asserted bychar-ready? cannotbe rubbed out. Ifchar-ready? were to return#f at end offile, a port at end of file would be indistinguishable from an interactiveport that has no ready characters.
Writes a written representation ofobj to the givenport. Stringsthat appear in the written representation are enclosed in doublequotes, andwithin those strings backslash and doublequote characters areescaped by backslashes.Character objects are written using the#\ notation.Write returns an unspecified value. Theport argument may be omitted, in which case it defaults to the valuereturned bycurrent-output-port.
Writes a representation ofobj to the givenport. Stringsthat appear in the written representation are not enclosed indoublequotes, and no characters are escaped within those strings. Characterobjects appear in the representation as if written bywrite-charinstead of bywrite.Display returns an unspecified value.Theport argument may be omitted, in which case it defaults to thevalue returned bycurrent-output-port.
Rationale: Write is intendedfor producing machine-readable output anddisplay is for producinghuman-readable output. Implementations that allow ``slashification''within symbols will probably wantwrite but notdisplay toslashify funny characters in symbols.
Writes an end of line toport. Exactly how this is done differsfrom one operating system to another. Returns an unspecified value.Theport argument may be omitted, in which case it defaults to thevalue returned bycurrent-output-port.
Writes the characterchar (not an external representation of thecharacter) to the givenport and returns an unspecified value. Theport argument may be omitted, in which case it defaults to the valuereturned bycurrent-output-port.
Questions of system interface generally fall outside of the domain of thisreport. However, the following operations are important enough todeserve description here.
Filename should be a string naming an existing filecontaining Scheme source code. Theload procedure readsexpressions and definitions from the file and evaluates themsequentially. It is unspecified whether the results of the expressionsare printed. Theload procedure does not affect the valuesreturned bycurrent-input-port andcurrent-output-port.Load returns an unspecified value.
Rationale: For portability,load must operate on source files.Its operation on other kinds of files necessarily varies amongimplementations.
Filename must be a string naming an output file to becreated. The effect oftranscript-on is to open the named filefor output, and to cause a transcript of subsequent interaction betweenthe user and the Scheme system to be written to the file. Thetranscript is ended by a call totranscript-off, which closes thetranscript file. Only one transcript may be in progress at any time,though some implementations may relax this restriction. The valuesreturned by these procedures are unspecified.