tags:

views:

352

answers:

3

I'm a newbie at Scheme, so forgive the question: I have a function that calculates the factorials of a list of numbers, but it gives me a period before the last number in the results. Where am I going wrong?

code:

#lang scheme

 (define fact
    (lambda (n)
      (cond
        ((= n 0) 1)
        ((= n 1) 1)
        (else (* n (fact (- n 1)))))))

 (define fact*
   (lambda (l)
     (cond
       ((null? (cdr l)) (fact (car l)))
       (else
        (cons (fact (car l)) (fact* (cdr l)))))))

output:

> (fact* '(3 6 7 2 4 5))
(6 720 5040 2 24 . 120)
+7  A: 

What you have done is create an improper list. Try this:

(define fact*
   (lambda (l)
     (cond
       ((null? (cdr l)) (list (fact (car l))))
       (else
        (cons (fact (car l)) (fact* (cdr l)))))))

The addition of the list in the fourth line should make this work as you expect. Better might be the following:

(define fact*
   (lambda (l)
     (cond
       (null? l) '())
       (else
        (cons (fact (car l)) (fact* (cdr l)))))))

This allows your fact* function to work on the empty list, as well as reducing the number of places where you make a call to fact.

Greg Hewgill
Thanks! Is there a way to do that with Scheme primitives? Is list a primitive?
Isaac Hodes
I edited to add the second implementation after your comment above. Does that answer your question?
Greg Hewgill
Ah, superb! My "Little Schemer" knowledge was forgotten momentarily. Thank you!
Isaac Hodes
I don't know if you know (or even if it's relevant), but there's a scheme primitive called `map`. What it does is apply a given function to every member of a given list. So you could define `fact*` as `(define fact* (lambda (l) (map fact l)))`, and it would work the same way. Of course, you should know how to do it without using `map` first!
configurator
D'oh! Just noticed exactly what I said as a later answer!
configurator
+1  A: 

Use append instead of cons. cons is used to construct pairs, which is why you have the "." that is used to separate the elements of a pair. Here's an example:

(define (factorial n)
  (if (<= n 1)
      1
      (* n (factorial (- n 1)))))

(define (factorial-list l)
  (if (null? l)
      '()
      (append (list (factorial (car l))) 
              (factorial-list (cdr l)))))
JG
Could you expand on that? Why exactly does the "." get inserted into my list when I cons an atom onto a list?
Isaac Hodes
A "pair" is an object that contains two atoms. A "list" is a sequence of pairs with the property that the `cdr` of each pair is another list, with the exception that the end of the list is indicated by the "empty list" atom. So `(cons 'a (cons 'b (cons 'c '())))` is a proper list, but `(cons 'a (cons 'b 'c))` is termed an "improper list" because the `cdr` of the second cons is not a list.
Greg Hewgill
Because in your final step, you are constructing a pair that consists of a list and an atom, e.g., `(cons '(20 21) 120)` => `((20 21) . 120)`. To make it work properly, you need to convert 120 into a list.
JG
That makes sense - thanks to the both of you!
Isaac Hodes
+2  A: 

The other answers have pointed out the reason why you get an improper list as a result of your fact* function. I would only like to point out that you could use the higher-order function map:

(define fact*
  (lambda (l)
    (map fact l))

(fact* '(3 6 7 2 4 5))

map takes a function and a list as arguments and applies the function to every element in the list, producing a new list.

Jonas