You want to determine whether a string is an integer
string->integer STRING START END -> INTEGER or #f
(define (string->integer str start end)
(and (< -1 start end (inc (string-length str)))
(let loop ((pos start) (accum 0))
(cond
((>= pos end) accum)
((char-numeric? (string-ref str pos))
(loop (inc pos) (+ (char->integer (string-ref str pos))
(- (char->integer #\0)) (* 10 accum))))
(else #f)))))
Here
inc is a macro or a function that returns the incremented
argument. On many Scheme systems, it can be implemented more efficiently than merely
(+ 1 x) if we assume that
x is a fixnum.
This procedure checks to see if a substring of
STRING from
START
(inclusive) till
END (exclusive) is a representation of a non-negative integer in decimal notation. If this is the case, this integer is returned.
Otherwise -- when the substring contains non-decimal characters, or when the range from
START till
END is not within
STRING, the result is
#f.
This procedure is a specialization of the standard
string->number. The latter is far more general: for example, it will try to read strings like "1/2", "1S2", "1.34" and even "1/0" (the latter causing a zero-divide error). Note that to
string->number, "1S2" is a valid representation of an inexact integer 100. Oftentimes we want to be more restrictive about what we consider a number: we want merely to read an integral label.
The obvious method is to use R5RS
string->number as a test.
string->number can convert strings containing base 2, 8, 10, and 16 numbers into a number. You can also force a conversion by formatting the number using Scheme conventions for specifying bases. If the conversion can't be performed,
string->number returns #f:
> (string->number "1234")
1234
> (string->number "abc" 16)
2748
> (string->number "77" 8)
63
> (string->number "abc" )
#f
> (string->number "123abc")
#f
> (string->number "778" 8)
#f
> (string->number "#o11" )
9
> (string->number "#x11" )
17
This is only half the solution, however.
string->number works for any kind of number, so technically, you should wrap the call in a call to the
integer? predicate:
> (integer? (string->number "77" ))
#t
> (integer? (string->number "77.7" ))
#f
However, the simple solution has some notable drawbacks. It must be noted that using
string->number for testing if a string
represents an integer has notable drawbacks. Scheme's concept of number is a lot broader than most languages', so
string->number is much more general than your typical programming language. Scheme understands arbitrarily large numbers like 2 ^ 80, fractions such as 1/2, and imaginary numbers:
> (string->number "1208925819614629174706176")
1208925819614629174706176
> (string->number "1/2")
1/2
> (string->number "0+1i")
0+1i
Scheme's number facilities can lead to surprises. For example, on Petite Chez Scheme and Gambit,
> (string->number "1S0")
1.0
> (integer? (string->number "1S0"))
#t
Not too many people would take "1S0" to mean an integer. It means an inexact integer.
There is even more serious problem. Often we test if a string
represents an integer when validating user input. It is
highly preferable if the test is a total predicate, that is,
generates no errors. However,
(string->number "1/0")
will raise a run-time error.
--
OlegK - 14 Sep 2004 (corrected solution)
--
GordonWeakliem - 23 Apr 2004 (simple solution)
http://pobox.com/~oleg/ftp/Scheme/util.html#misc-str-util
Oleg, two points: your text is below the STOPINCLUDE directive, which is where comments are supposed to live; this page would be more useful to the reader if you worked your correct solution into the body of the text with Gordon's solution. I don't have time right now to make these changes or I would do them myself --
NoelWelsh - 14 Sep 2004
I moved STOPINCLUDE; thank you for clarifying it. I thought about
how to merge two solutions, and the best I have come with is to
add one level of headers, and make a forward reference. Any hints
how to do the merging better?
Both solutions will work, depending on circumstances
--
OlegK - 14 Sep 2004
My preference is to give the
correct solution (i.e. Oleg's) under solution and put the rest under discussion. In this case, I put the discussion of the correct solution first and then went into a discussion of Scheme's number system, which leads into why
string->number is insufficient. Also, the definition of
inc seems worthy of a recipe of its own under
NumberRecipes, I think.
--
GordonWeakliem - 14 Sep 2004
Note that
inc is called
add1 in
MzScheme. In my opinion the portable solution
(+ x 1) is in the context of a cookbook better (easier to grasp). Besides, most compilers ought to recognize this pattern anyway.
--
JensAxelSoegaard - 14 Sep 2004
Thank you both! Perhaps indeed one ought to use
(+ x 1)
in the context of the cookbook. Regarding compiler's recognizing that pattern: Gambit and Bigloo are pretty good Scheme compilers. And yet,
if one looks into their source code, one sees
(##fixnum.+ 1 index) (in Gambit) or
(+fx 1 escape-mark) (in Bigloo) all over the place. Incidentally, OCaml has
int->int
primitives
succ and
pred, which are heavily used in its own
source code. That is a bit surprising considering that arithmetical
operations in Ocaml are monomorphic and 1 + x is easier to type.
--
OlegK - 15 Sep 2004