0

I've found out how to use foldr and lambda to find the number of 1's in a list. But how to use an if condition or any other method to verify if the list has only one 1.

(define (exactlyone L)
  (foldr (lambda (elem count) (if (equal? elem 1) (+ count 1) count))
        0 L)

)  

How can use the count value in an if condition if possible?

HasaniH
  • 8,232
  • 6
  • 41
  • 59
anush95
  • 71
  • 1
  • 1
  • 5

3 Answers3

2

You can't be sure of the number of 1s until you traverse all the list, so necessarily foldr must consume all the items. After that, it's just a simple matter of testing if the returned value of count was 1:

(define (exactlyOne L)
  (= 1
     (foldr (lambda (elem count)
              (if (equal? elem 1)
                  (+ count 1)
                  count))
            0
            L)))

Of course, the simplest way would be to use existing procedures (such as count), instead of reinventing the wheel. This will work in Racket:

(define (exactlyOne lst)
  (= 1
     (count (curry equal? 1) lst)))

For example:

(exactlyOne '(1 2 3))
=> #t

(exactlyOne '(1 2 3 1))
=> #f
Óscar López
  • 232,561
  • 37
  • 312
  • 386
0

The easiest way would be to do your own recursion:

(define (only-one predicate lst)
  (let loop ((lst lst) (seen #f))
    (cond ((null? lst) seen)
          ((not (predicate (car lst))) (loop (cdr lst) seen))
          ;; from here on we know predicate is true
          (seen #f) ; at least two
          (else (loop (cdr lst) #t))))) ; recurse with seen as #t

If you want to solve it with a fold you can:

(define (only-one predicate lst)
  (call/cc
   (lambda (return)
     (foldl (lambda (e seen)
              (cond ((not (predicate e)) seen) ; keep seen (whatever it is)
                    (seen (return #f))         ; short circuit at second match
                    (else #t)))                ; change seen to #t
            #f
            lst))))

This uses call/cc to get an exit procedure in case we know the result before all the elements are processed. It will be #f if none or more than one matches and #t if there is exactly once.

Both works like this:

(only-one (lambda (x) (equal? x 1)) '(0 1 2 3 4 5))   ; ==> #t
(only-one (lambda (x) (equal? x 1)) '(2 3 4 5))       ; ==> #f
(only-one (lambda (x) (equal? x 1)) '(0 1 2 1 3 4 5)) ; ==> #f
Sylwester
  • 47,942
  • 4
  • 47
  • 79
0

This problem can be solved if you allow the kernel function of the fold to capture a lexical scope containing mutable variables. Then you can keep the accumulator type Boolean, and yet have enough state to do the calculation.

Pseudocode:

(foldl (let ([s 0])
        (lambda (accum item)
          ;; if (equal? item 1) and s is not already 2
          ;;   increment s
          ;; return (equal? s 1)
         )
       #f list)

You cannot solve this with a function that doesn't capture any environment; but why restrict to that? A function is code plus a lexical environment, by definition.

In the above, the accumulator is basically a dummy; we don't even look at it, because the state s represents everything we need. We could use a Boolean s such that the state is a combination of the accumulator param and the information in s. We could divide the state between a Boolean accumulator and a Boolean s (so that they together form a two-bit counter representing the necessary three states).

Here is an informal proof that it can't be solved with just a Boolean-returning function without a mutable environment:

  1. Observe that the result has to be Boolean: is there exactly one 1, true or false? So the function we use as the fold kernel must have a Boolean accumulator, since the accumulator is what is returned.

  2. The accumulator of the fold encapsulates the entire state on which the kernel function's algorithm makes the decision. If, for instance, the function makes use of a lexical scope which contains mutable variables, that would be cheating.

  3. The algorithm requires at least three states in the accumulator. The accumulator must be in some initial S0, from which it transitions to S1 when a 1 is seen, from which it transitions to S2 when another 1 is seen. This accumulator must then be interpreted outside of the fold as S0 and S2 denoting false, and S1 true.

  4. Though we could, in theory, change the accumulator type between visited items, we have no information for this; we are not informed which element is last. If we knew that we are looking at the last element, we could lapse our tri-state accumulator to a Boolean value and return that.

This is why the second part of Sylwester's answer uses continuations: then the algorithm, instead of transitioning to S2 can escape out of the fold directly and produce a false; the accumulator can then be Boolean. (A much simpler non-local exit mechanism would suffice in place of full blown continuations, such as a return from a lexical block).

Kaz
  • 55,781
  • 9
  • 100
  • 149