2

I am looking for a built-in function in Racket that will return True iff all the items in a list are true.

I tried:

(define (all lst)
  (when 
      (equal? lst '()) 
      #t)
  (if (not (car lst))
      #f
      (all (cdr lst))))

Giving error:

car: contract violation
  expected: pair?
  given: '()

A couple of testcases:

(all '(#t #f #t)) ; #f
(all '(#t #t #t)) ; #t

Could you please either fix it or point me to the built-in function? (I googled, but got no meaningful result)

ben rudgers
  • 3,647
  • 2
  • 20
  • 32
Caridorc
  • 6,222
  • 2
  • 31
  • 46

4 Answers4

5

You've already accepted another answer that explains a nice way to do this, but I think it's worth pointing out what was wrong in your attempt, because it was actually very close. The problem is that true from the when block is completely ignored. It doesn't cause the function to return. So even when you have the empty list, you evaluate the when, and then keep on going into the other part where you call car and cdr with the same empty list:

(define (all lst)
  (when                  ;  The whole (when ...) expression
      (equal? lst '())   ;  is evaluated, and then its result
      #t)                ;  is ignored.
  (if (not (car lst))   
      #f
      (all (cdr lst))))

A very quick solution would be to change it to:

(define (all lst)
  (if (equal? lst '())  
      #t               
      (if (not (car lst))   
          #f
          (all (cdr lst)))))

At that point, you can simplify a little bit by using boolean operators rather than returning true and false explicitly, and clean up a little bit by using empty?, as noted in the other answer:

(define (all lst)
  (or (empty? lst)  
      (and (car lst)
           (all (cdr lst)))))

You were actually very close at the start.

Sylwester
  • 47,942
  • 4
  • 47
  • 79
Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
  • Is `when` not returning a bug or the intended behaviour of the keyword? I though `when` was just a 'one armed' version of `if`. – Caridorc May 03 '15 at 14:13
  • (when expr1 expr2) is short for (if expr1 expr2 (void)) – soegaard May 03 '15 at 15:56
  • 1
    And if you are curious, why one-armed ifs were removed from Racket: http://stackoverflow.com/q/10863192/23567 – soegaard May 03 '15 at 16:03
  • 1
    @Caridorc `(when test condition)` is short for a one-armed version, so to speak, but an ìf` doesn't terminate the procedure either. E.g., `(define (foo) (if #t 1 2) (if #t 3 4))` returns 3, not 1. The first if expression, `(if #t 1 2)` evaluates to true, but then the evaluation of `foo` continues to `(if #t 3 4)`, which evaluates to `3`. In the same way `(define (foo) (when #t 1) (if #t 3 4))`, which is like your original code, produces 3, not 1. – Joshua Taylor May 03 '15 at 22:10
5

If you're looking for a builtin solution, you'll probably want to take a look at andmap, which applies a predicate over an entire list and ands the results together.

You could use this to implement all very simply.

(define (all lst)
  (andmap identity lst))

By using identity from racket/function, all will just use the values in the list as-is. Instead of using identity explicitly, you could also use values, which is just the identity function on single values, so it's a somewhat common idiom in Racket.

Alexis King
  • 43,109
  • 15
  • 131
  • 205
  • `andmap` is not available in most Scheme implementations. In addition, it does not always return `#t` but rather something that evaluates to `#t`. I'm not sure it meets the letter of the specification in the question even if it might meet the spirit. – ben rudgers May 04 '15 at 03:23
  • 1
    @benrudgers Well, the question does explicitly specify Racket, and it also requests a built-in implementation if one exists, so here it is. It's also easy to convert from a truthy/falsy value to a boolean, but usually it's not necessary. – Alexis King May 04 '15 at 03:28
  • It is also tagged "scheme". Otherwise I wouldn't have worried about some poor schemer stumbling onto `andmap` two years from now only to find it wasn't available. `Andmap`'s evaluation is a bit of a bigger issue since `(+ 1 (andmap '(#t)))` throws `exn:fail:contract` but `(+1 (andmap '(1)))` evalutes to `2`. It's a bit of a latent bug of the sort that makes for black-box-abstraction unhappiness. YMMV. – ben rudgers May 04 '15 at 04:06
3

There are two kinds of lists: empty ones and pairs.

Therefore we have the following structure:

(define (all xs)
  (cond
    [(empty? xs) ...]
    [(pair?  xs) ...]
    [else        (error 'all "expected a list, got: " xs)]))

Since all elements in the empty list are true, we get:

(define (all xs)
  (cond
    [(empty? xs) #t]
    [(pair?  xs) ...]
    [else        (error 'all "expected a list, got: " xs)]))

If a list begins with a pair, then all elements of the list are true, if both the first element of the list and the rest of the elements of the list are true:

(define (all xs)
  (cond
    [(empty? xs) #t]
    [(pair?  xs) (and (first xs) (all (rest xs)))]
    [else        (error 'all "expected a list, got: " xs)]))

Note that part of the problem in your program is the use of when.

The result of

(when #t
   'foo)
'bar

is 'bar. The construct when is only useful if you are using side effects (such as caused by set! and friends).

soegaard
  • 30,661
  • 4
  • 57
  • 106
1

All is a higher order folding function. Scheme refers to these as "reductions" and reduce is available in SRFI-1

In Gauche Scheme:

(use srfi-1)
(define (all list-of-x)
  (reduce (lambda (x y) 
            (and x y))
          #t
          list-of-x))

Will return #f or a value that evaluates to true. For example:

gosh> (all '(1 2 3))
1

If that's OK, then we're done. Otherwise we can always get #t with:

(use srfi-1)
(define (all-2 list-of-x)
  (if (reduce (lambda (x y)
        (and x y))
          #t
          list-of-x)
      #t
      #f))

And then wind up with:

gosh> (all '(1 2 3))
#t
ben rudgers
  • 3,647
  • 2
  • 20
  • 32