/ StringChapter / Cookbook.StringNumber

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

`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

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)

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

CookbookForm | |
---|---|

TopicType: | Recipe |

ParentTopic: | StringRecipes |

TopicOrder: | 200 |

The copyright for certain compilations of material taken from this website is held by the SchematicsEditorsGroup - see ContributorAgreement & LGPL.

Other than such compilations, this material can be redistributed and/or modified under the terms of the GNU Lesser General Public License (LGPL), version 2.1, as published by the Free Software Foundation.

Ideas, requests, problems regarding Schematics Cookbook? Send feedback. / You are Main.guest