Racket
You can write a dead-simple version of map-index using the Racket range procedure and mapping over the result:
(define (map-index-1 f xs)
(map f xs (range (length xs))))
In some situations you might want the indices first:
(define (map-index-2 f xs)
(map f (range (length xs)) xs))
If you want to be able to use map-index over multiple lists, you can pass the list arguments to an optional parameter. Here, apply applies the map procedure to a list constructed from the function f, the input lists, and a range list:
(define (map-index-3 f . xs)
(apply map (cons f
(append xs
(list (range (length (car xs))))))))
But it might make more sense to place the indices first when mapping over multiple lists:
(define (map-index-4 f . xs)
(apply map (cons f
(cons (range (length (car xs)))
xs))))
scratch.rkt> (map-index-1 cons '(a b c d))
'((a . 0) (b . 1) (c . 2) (d . 3))
scratch.rkt> (map-index-2 cons '(a b c d))
'((0 . a) (1 . b) (2 . c) (3 . d))
scratch.rkt> (map-index-3 list '(a b c d) '(one two three four) '(w x y z))
'((a one w 0) (b two x 1) (c three y 2) (d four z 3))
scratch.rkt> (map-index-4 list '(a b c d) '(one two three four) '(w x y z))
'((0 a one w) (1 b two x) (2 c three y) (3 d four z))
Scheme
Standard Scheme does not have a built-in range procedure, but it is easy enough to write a simple version. These solutions will work on any R4RS, R5RS, R6RS, or R7RS Scheme implementation. This version of range does more than is required for the current application, taking a step argument which can be positive or negative:
(define (range start stop step)
(if (or (and (> step 0)
(>= start stop))
(and (<= step 0)
(<= start stop)))
'()
(cons start (range (+ start step) stop step))))
Having defined a range procedure, the same approach used for the Racket solutions above can be used in Scheme:
(define (map-index-5 f xs)
(map f xs (range 0 (length xs) 1)))
(define (map-index-6 f xs)
(map f (range 0 (length xs) 1) xs))
(define (map-index-7 f . xs)
(apply map (cons f
(append xs
(list (range 0 (length (car xs)) 1))))))
(define (map-index-8 f . xs)
(apply map (cons f
(cons (range 0 (length (car xs)) 1)
xs))))
> (map-index-5 cons '(a b c d))
((a . 0) (b . 1) (c . 2) (d . 3))
> (map-index-6 cons '(a b c d))
((0 . a) (1 . b) (2 . c) (3 . d))
> (map-index-7 list '(a b c d) '(one two three four) '(w x y z))
((a one w 0) (b two x 1) (c three y 2) (d four z 3))
> (map-index-8 list '(a b c d) '(one two three four) '(w x y z))
((0 a one w) (1 b two x) (2 c three y) (3 d four z))
A map-range Procedure for Standard Scheme
This method can be extended to use numbers in a more complex range that may not represent indices by taking advantage of the capabilities of a range function. Using the range definition from above, this map-range procedure still works on R4RS to R7RS Scheme implementations:
(define (map-range f start step . xs)
(let ((stop (+ start (* step (length (car xs))))))
(apply map (cons f
(cons (range start stop step)
xs)))))
> (map-range cons 2 2 '(a b c d))
((2 . a) (4 . b) (6 . c) (8 . d))
> (map-range list 5 5 '(a b c d) '(one two three four) '(w x y z))
((5 a one w) (10 b two x) (15 c three y) (20 d four z))
> (map-range cons 2 -2 '(a b c d))
((2 . a) (0 . b) (-2 . c) (-4 . d))
> (map-range list 5 -5 '(a b c d) '(one two three four) '(w x y z))
((5 a one w) (0 b two x) (-5 c three y) (-10 d four z))