10

How to create a list of consecutive numbers in Scheme?

In Python to create a list of integers from 1 to 10 would be range(1,11). Is there an equivalent for Scheme?

mzscheme --version gives Welcome to Racket v5.2.1.

Edit: Per https://stackoverflow.com/a/7144310/596361 to implement range functionality, this code is needed:

#lang racket
(require srfi/1)
(iota 5 1)
Community
  • 1
  • 1
Mirzhan Irkegulov
  • 17,660
  • 12
  • 105
  • 166

7 Answers7

13

Look for iota (as defined in SRFI-1).

Example: (iota 10 1) gives 10 consecutive integers starting from 1 (instead of the default of 0).

iota doesn't take the same arguments as range but it duplicates all the functionality - ascending ranges, descending ranges, starting from 0 if only one bound is given, ability to specify the interval.

Mirzhan Irkegulov
  • 17,660
  • 12
  • 105
  • 166
Sven
  • 496
  • 4
  • 9
  • 1
    I am no expert, but range in python3 returns a range object and it evaluates it lazily through next function, following the generator protocol. I see I can do (delay (iota 5 1)) and later force can evaluate it. My question is: once evaluated, all the list is put at once in memory? Can exactly the same effect as python's range be obtained? I mean, evaluating one value at a time. – Germán Diago Jan 30 '17 at 17:16
5

Here's a version which does an ascending range if the first number is lower or a descending range if it is higher:

(define range
  (lambda (n m)
    (cond
      ((= n m) (list n))
        (else (cons n (range ((if (< n m) + -) n 1) m))))))

And here's an improved version which can take 1 or 2 arguments; if only one is given, it does a range from 0 to the given number:

(define range
  (lambda (n . m)
    (let
      ((n (if (null? m) 0 n)) (m (if (null? m) n (car m))))
      (cond
    ((= n m) (list n))
    (else (cons n (range ((if (< n m) + -) n 1) m)))))))
itsbruce
  • 4,825
  • 26
  • 35
5

There is a built-in range function in Racket that behaves like that of Python.

> (range 10)
'(0 1 2 3 4 5 6 7 8 9)
ayberkt
  • 105
  • 1
  • 5
3

If there's nothing built-in, it's trivial to write your own:

(define (range first last)
  (if (>= first last)
      '()
      (cons first (range (+ first 1) last))))

Online scheme evaluator: http://eval.ironscheme.net/?id=71

nibot
  • 14,428
  • 8
  • 54
  • 58
  • `(cons first (range ((if (< first last) + -) first 1) last))))` – itsbruce Oct 16 '12 at 08:51
  • @itsbruce That never terminates. – nibot Oct 16 '12 at 09:00
  • Yes it does. Try it. It gives a descending range if the first number is higher than the lower, or an ascending range if the first is lower. Ah, you would have to change your condition to (if (= first last)) – itsbruce Oct 16 '12 at 09:07
  • 1
    To mimic the Python behavior, I think `range` should return an empty list if `last <= first`. – nibot Oct 16 '12 at 09:33
  • But to be a true analog, you should stop at n-1 *and* offer a single argument version where the range starts at 0 – itsbruce Oct 16 '12 at 10:01
  • And you're wrong about the Python function. Check the documentation - it will do descending ranges if you give it decreasing arguments. – itsbruce Oct 16 '12 at 10:40
1

I'm just elevating @Ankur's comment to an answer. In Racket, you have "in-range":

#lang racket

(in-range 7)  ;; produces #<stream>

;; used in a loop:
(for/list ([i (in-range 7)])
  i)
;; produces (list 0 1 2 3 4 5 6)


;; and, for the lazy among us:

(for/list ([i 7])
  i)

;; produces the same

It can also accept a lower limit, an increment (including negative), etc.

John Clements
  • 16,895
  • 3
  • 37
  • 52
0

Following a comment by Germán Diago I made a pure functional lazy version (i.e. stream) for this. It can construct a range stream of any size that your Scheme implementation can handle in constant time as well as access the current element and advance the stream, also in constant time.

(define ^range
    (lambda (x y getter)
      (op x y getter)))

(define EOS ; End of stream for finite streams
    (^range '() '() (lambda () EOS)))

(define range 
    (lambda (x . y) ; if y < x then stream is infinite
      (let ((x (if (null? y) 0 x))
            (y (if (null? y) x (car y))))
        (^range x y (lambda ()
                      (if (= x y) EOS 
                       (range (+ x 1) y)))))))

(define get ; Get current element
    (lambda (r)
      (r (lambda (x y g) x))))

(define next ; Get stream for next element
    (lambda (r)
      (r (lambda (x y g) (g)))))

Using this code:

> (define r (range 1 3))
> (get r)
1
> (get (next r))
2
> (get (next (next r)))
3
> (get (next (next (next r)))) ; EOS
()
Neowizard
  • 2,981
  • 1
  • 21
  • 39
-1

Not finding what I wanted and not wanting to have to use an external package, I ended up writing my own version which differs from the python version (hopefully improving on it). If you think it is really inefficient and can improve on it, please do.

;; A version of range taking the form (range [[first] last [[step]]] ).
;; It takes negative numbers and corrects STEP to the same direction
;; as FIRST to LAST then returns a list starting from FIRST and
;; ending before LAST
(define (range . args)
  (case (length args)
   ( (0) '())
   ( (1) (range 0 (car args) (if (negative? (car args)) -1 1)))
   ( (2) (range (car args) (cadr args)
             (if (>= (car args) (cadr args)) -1 1)))
   ( (3) (let* ((start (car args)) (end (cadr args)) 
                (step (if (> start end)
                          (- (abs (caddr args))) 
                          (abs (caddr args)))))
            (let loop ((x start) (xs '()))
               (cond ((and (>= end start) (<= end x))
                      (reverse xs))
                     ((and (<= end start) (>= end x))
                      (reverse xs))
                     (else (loop (+ x step) (cons x xs)))))))
   (else (error 'range "too many arguments"))))
;  (else (display "ERROR: range too many arguments") (newline)))) ;;r4rs

;; (range-inc [[first] last [[step]]] ) includes LAST in the returned range
(define (range-inc . args)
  (case (length args)
    ( (0) '())
    ( (1) (append (range (car args)) args))
    ( (2) (append (range (car args) (cadr args)) (cdr args)))
    ( (3) (append (range (car args) (cadr args) (caddr args))
                  (list (cadr args))))
    (else (error 'range "too many  arguments"))))
;   (else (display "ERROR: range too many arguments") (newline)))) ;;r4rs

Note I wrote a common lisp version as well

Kyuvi
  • 360
  • 3
  • 13