3

I am trying to learn Scheme, and I encounter a problem. I am able to make a recursive function to create a list like (5,4,3,2,1). But I don't know how to create a function (let's say (define (make_list n)) ) that has ascending order of elements likes (1,2,3,4,5....n). Someone some hint?

 (define (make_list n)
  (if (= n 1)
      (list 1)
  (append (list n) (make_list (- n 1)))))

So this is how I make a list with n element, and it returns

> (make_list 3)
'(3 2 1)
> (make_list 5)
'(5 4 3 2 1)

What I need is to return something like:

> (make_list 3)
'(1 2 3)
> (make_list 5)
'(1 2 3 4 5)
KamiGasai
  • 43
  • 4
  • 1
    I guess you're expected to write the solution from scratch, but the idiomatic solution would be: `(build-list n add1)` – Óscar López Dec 10 '17 at 16:45

6 Answers6

3

You can make a list of ascending elements with the range function:

> (range 1 20)
'(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)

If you want to iterate over this list consider using in-range instead, which will generate the list items as needed, e.g.:

> (in-range 1 20)
#<stream>
> (for ([i (in-range 1 20)])
       (displayln (* i 10)))
10
20
30
...

However usually the likes of map, filter and fold are better ways to handle iteration.

Read the documentation here: https://docs.racket-lang.org/reference/pairs.html#%28def._%28%28lib._racket%2Flist..rkt%29._range%29%29

Eric Clack
  • 1,886
  • 1
  • 15
  • 28
2

You should avoid using append where it is not necessary. Append adds elements to the back of your list, for which it first needs to traverse the entire list to find the end. Cons does not need to do this, it can just add elements to the front immediately.

Both an ascending list and a descending one can be created using just cons.

(define (desc-list n)
   (if (= n 1)
      (list 1)
      (cons n (desc-list (- n 1)))))

> (desc-list 5)
'(5 4 3 2 1)

(define (asc-list from to)
   (if (> from to)
      '()
      (cons from (asc-list (+ from 1) to))))

> (asc-list 1 5)
'(1 2 3 4 5)

Though you could use append if you really want too. All you have to do to get what you want then is reverse the parameters you pass to append.

(define (make_list n)
   (if (= n 1)
      (list 1)
      (append (make_list (- n 1)) (list n))))

> (make_list 5)
'(1 2 3 4 5)

So you were very close.

Aaron L
  • 96
  • 1
  • 4
  • Yes that was really close! I tried `append (make_list (- n 1)) (list n))` before but it seems I got messed with the parentheses. – KamiGasai Dec 10 '17 at 21:36
1

Based on your current definition you have

(make-list 5) = (append (list 5) (make-list 4))
              = { 5 } + { 4 ..... 1 }

but you want

(make-list 5) = { 1 ..... 4 } + { 5 }
              = (append  ...  ...)

so to change the order of the result you just need to change the order in which you are creating the result.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
1

Don't make a list one element a time with append. append appends two lists linear time so if you do that for each element in an argument you might make it quadratic time.

Lists are cons-ed from end to beginning. If you want to make a list 0..n you need to count downwards from n while you are accumulating, like this:

(define (make-list n)
  (define (helper n acc)
    (if (< n 0)
        acc
        (helper (- n 1) (cons n acc))))
  (helper n '()))

Sometimes you cannot do that and then you usually would first make the list in reverse, then reverse it. that is two linear time operations making the end result linear too:

(define (sum lst1 lst2)
  (let helper ((lst1 lst1) (lst2 lst2) (acc '()))
    (if (null? lst1)
        (reverse acc)
        (helper (cdr lst1) 
                (cdr lst2)
                (cons (+ (car lst1) (car lst2)) acc)))))
Sylwester
  • 47,942
  • 4
  • 47
  • 79
1

Just adding to this thread that srfi-1 contains the iota function

; (iota numElems [start] [step])

> (iota 10)
(0 1 2 3 4 5 6 7 8 9)
> (iota 10 0 2)
(0 2 4 6 8 10 12 14 16 18)
> (iota 10 10 1)
(10 11 12 13 14 15 16 17 18 19)
ramrunner
  • 1,362
  • 10
  • 20
0

Think about using a sub-procedure. You could use your code as the sub-procedure and just reverse the result in the main procedure:

(define (make-list n)
  (define (sub n)
    (if (= n 1)
        (list 1)
        (append (list n) (sub (- n 1)))))
  (reverse (sub n)))

Alternatively, and more efficiently, you can use an accumulator (called res in this example); it uses cons rather than append, and doesn't need an extra reverse step:

(define (make-list n)
  (define (sub n res)
    (if (< n 1)
        res
        (sub (- n 1) (cons n res))))
  (sub n null))
uselpa
  • 18,732
  • 2
  • 34
  • 52