2

I'm trying to write a scheme function that will return the unique atoms found in the input list such that.

> (unique-atoms '(a (b) b ((c)) (a (b))))
(a c b)
> (unique-atoms '(a . a))
(a)
> (unique-atoms '())
()

I was thinking something like this as a start

(define (unique-atoms l)
  (if (null? l)
      '()
   (eq? (car (l) unique-atoms(cdr (l))))))

but I don't know how to collect the atoms that are unique, and create a new list while checking everything recursively.

Yoink
  • 167
  • 2
  • 11

4 Answers4

1

This problem has two parts:

  1. You need to find a way to visit each element of the given form, recursing into sublists.
  2. You need a way to collect the unique elements being visited.

Here's a solution to the first part:

(define (recursive-fold visitor initial x)
  (let recur ((value initial)
              (x x))
    (cond ((null? x) value)
          ((pair? x) (recur (recur value (car x)) (cdr x)))
          (else (visitor x value)))))

I leave it for you to implement the second part.

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • Could you explain the arguments that this function takes and what they are for respectively? – Yoink Jun 30 '13 at 02:43
  • Sure, it takes the same arguments as `fold`. In fact, it's a left fold, except that sublists are traversed also. If you haven't used `fold` before, the first argument (`visitor`) is a procedure that takes two arguments, let's call them `elem` and `last-value`. For each element in your given list, your procedure is called once, with `elem` bound to that element, and `last-value` bound to the value returned by the previous call to your procedure (or `initial` if this is the first call). `x` is the list you're traversing. – C. K. Young Jun 30 '13 at 03:07
  • I'm sorry, I still have a hard time understanding how to work with this function and what its benefit it. I don't know what to give for test arguments to see what this function is doing. – Yoink Jun 30 '13 at 03:33
  • Ah, so you haven't learnt about `fold` yet, or else its benefits would have been immediately obvious to you. (It's one of the most powerful operations in functional programming, and it's well worth your while to learn.) That's unfortunate, because I've been meaning to write an article on `fold` for a long time. In fact, here's a preamble that's supposed to lead up to my `fold` article: http://dyscour.se/post/10113691502/5-ways-to-flatten-part-1 – C. K. Young Jul 01 '13 at 02:32
1

The following walks list, term by term. If the next value is a list itself, then a recursive call is made with (append next rest) - that is, as list is walked we are flattening sublists at the same time.

We use a (tail) recursive function, looking, to walk the list and to accumulate the rslt. We add to the result when next is not alreay in rslt.

(define (uniquely list)
  (let looking ((rslt '()) (list list))
    (if (null? list)
        rslt
        (let ((next (car list))
              (rest (cdr list)))
          (if (list? next)
              (looking rslt (append next rest))
              (looking (if (memq next rslt)
                           rslt
                           (cons next rslt))
                       rest))))))
> (uniquely '(a b (a b) ((((a))))))
(b a)

If you really want the code to work for 'improper lists' like '(a . a) then the predicates null? and list? probably need to change.

GoZoner
  • 67,920
  • 20
  • 95
  • 145
0

I found a half solution where the non unique items are removed, although this wont work for an atom b and a list with b such as '(b (b))

(define (uniqueAtoms l)
  (cond ((null? l)
         '())
        ((member (car l) (cdr l))
         (uniqueAtoms (cdr l)))
        (else
         (cons (car l) (uniqueAtoms (cdr l))))))
Yoink
  • 167
  • 2
  • 11
  • Good, it sounds like you have your half of the solution implemented. You just need a way to wire it up to my `recursive-fold` and you're in to win! – C. K. Young Jul 01 '13 at 02:30
-1

The easiest way to solve this problem with all kinds of list structures is to divide it into two parts

1) flatten then list - this results in a proper list with no sublists

; if you use Racket, you can use the build-in flatten procedure
; otherwise this one should do
(define (flatten expr)
  (let loop ((expr expr) (res '()))
    (cond 
      ((empty? expr) res)
      ((pair? expr)  (append (flatten (car expr)) (flatten (cdr expr))))
      (else          (cons expr res)))))

2) find all unique members of this proper list

(define (unique-atoms lst)
  (let loop ((lst (flatten lst)) (res '()))
    (if (empty? lst)
        (reverse res)
        (let ((c (car lst)))
          (loop (cdr lst) (if (member c res) res (cons c res)))))))

Tests:

; unit test - Racket specific
(module+ test
  (require rackunit)
  (check-equal? (unique-atoms '(a (b) b ((c)) (a (b)))) '(a b c))
  (check-equal? (unique-atoms '(a (b) b ((c . q)) (a (b . d)))) '(a b c q d))
  (check-equal? (unique-atoms '(a . a)) '(a))
  (check-equal? (unique-atoms '(a b (a b) ((((a)))))) '(a b))
  (check-equal? (unique-atoms '()) '()))
uselpa
  • 18,732
  • 2
  • 34
  • 52
  • It's my life mission to eradicate all `append`-based flatten implementations. Sorry yours has been targeted, too. More explanation: http://stackoverflow.com/a/13548087/13 – C. K. Young Jul 03 '13 at 03:23
  • Seriously? This is not a performance-related question, and append is much easier to understand for a newbie than any fold-related solution. – uselpa Jul 03 '13 at 04:03