August 3, 2018

We begin with a function that increments a character:

(define (next-char c)
  (integer->char (+ (char->integer c) 1)))

Next we have a function that increments a single odometer position, which can be either a letter or digit, and returns both the next letter or digit and an indication if there is a carry:

(define (next1 c)
  (cond ((char=? c #\Z) (values #\A #t))
        ((char=? c #\9) (values #\0 #t))
        (else (values (next-char c) #f))))

Finally we have a function that increments each character, starting from the least significant, until there is no carry:

(define (next str)
  (let loop ((cs (reverse (string->list str))) (xs (list)) (carry? #f))
    (if (null? cs) (list->string (reverse xs))
        (lambda () (next1 (car cs)))
        (lambda (x carry?)
          (if carry?  (loop (cdr cs) (cons x xs) #f)
            (list->string  (append (reverse (cdr cs)) (cons x xs)))))))))

Here are some examples:

> (next "123")
> (next "199")
> (next "999")
> (next "ABC")
> (next "FZZ")
> (next "G7TS39")

You can run the program at https://ideone.com/vDHSfC.


Pages: 1 2

5 Responses to “Odometer”

  1. Paul said

    In Python.

    from string import ascii_uppercase, digits
    SUCC = {a: b for a, b in zip(ascii_uppercase, ascii_uppercase[1:] + "A")}
    SUCC.update({a: b for a, b in zip(digits, digits[1:] + "0")})
    def odo_inc(data):
        datalist = list(data)
        for j, c in reversed(list(enumerate(datalist))):
            datalist[j] = succ = SUCC[c]
            if succ not in "0A":
        return "".join(datalist)
    print(odo_inc("1Z9"))     # -> "2A0"
    print(odo_inc("Z9ZZ99"))  # -> "A0AA00"
    print(odo_inc("G7TZ99"))  # -> "G7UA00"
  2. Daniel said

    Here’s a solution in C.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    int main(int argc, char* argv[]) {
        if (argc != 2) {
            fprintf(stderr, "Usage: %s STR\n", argv[0]);
            return EXIT_FAILURE;
        size_t n = strlen(argv[1]);
        int carry = 1;
        for (int i = n - 1; i >= 0; --i) {
            if (!carry) break;
            switch(argv[1][i]) {
                case 'z':
                case 'Z':
                    argv[1][i] -= 25;
                case '9':
                    argv[1][i] = '0';
                    carry = 0;
        printf("%s\n", argv[1]);
        return EXIT_SUCCESS;


    $ ./odometer G7TS39
    $ ./odometer G7TZ99
    $ ./odometer Z9ZZ99
    $ ./odometer 1Z9
  3. Mike said


    Because everything is better with a regular expression. ;-)

    import functools, re
    odo = functools.partial(re.sub, '(.)(?=[9Z]*$)', lambda m: '0' if m[1]=='9' else 'A' if m[1]=='Z' else chr(ord(m[1])+1))

    It works by replacing anything that is followed by nothing but ‘9’s and ‘Z’s. Replace it with a ‘0’ if it was a ‘9’, by an ‘A’ if it was a ‘Z’, or by the next character in sequence otherwise.

  4. Steve said

    @ProgrammingPraxis: Submitted version for Mumps. Haven’t seen it.

  5. Mumps version

    vista@steve-VirtualBox:~/EHR/r$ cat ODOMETER.m 
    GO(STR)  ;
             I STR'?1.AN Q STR_" not alphanumeric"
    	 D GO2(.STR,$L(STR),1)
    	 Q STR
             Q:'POS  ; No more levels to process
    	 S CHAR=$E(STR,POS)
    	 I CARRY D
    	 . I CHAR?1(1"Z",1"9") D  ; Handle end of letters and digits
    	 . . S $E(STR,POS)=$S(CHAR="Z":"A",1:0)
    	 . . S CARRY=1
    	 . E  D
    	 . . S $E(STR,POS)=$C($A(CHAR)+1)
    	 . . S CARRY=0
    	 E  D  ; No more carries
    	 . S CARRY=0
             D GO2(.STR,$S(CARRY:POS-1,1:0),CARRY)
    vista@steve-VirtualBox:~/EHR/r$ gtm
    GTM>F I=123,199,999,"ABC","FZZ","G7TS39" W !,I," --> ",$$GO^ODOMETER(I)
    123 --> 124
    199 --> 200
    999 --> 000
    ABC --> ABD
    FZZ --> GAA
    G7TS39 --> G7TS40

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: