racket

How to handle arbitrary amount of quoted arguments?


I want a function that handles an arbitrary amount of quoted symbols.

(define (f . args)
  (if (empty? args)
      '()
      (match (first args)
        ['one   (cons 1 (f (rest args)))]
        ['two   (cons 2 (f (rest args)))]
        ['three (cons 3 (f (rest args)))])))

Running this however

(f 'one 'two 'three)

gives an error

match: no matching clause for '(two three)

Obviously I'm misunderstanding something about how quotes, symbols, datums, etc. all work. How would you write a function that does this?


Solution

  • When you call f recursively in the match clauses, you're passing a list as its only argument. Because the function is defined with a dot-rest lambda list, args is a list of one element, that is itself a list (arg is '((two three)) for example), that fails all of the match conditions.

    One fix is to use (apply f (rest args)) when recursing. Or you could have a helper function that takes a single list argument that the top level f calls that calls itself, not f, when it recurses.

    (define (f . args)
      (define (helper args)
        (if (empty? args)
            '()
            (match (first args)
              ['one   (cons 1 (helper (rest args)))]
              ['two   (cons 2 (helper (rest args)))]
              ['three (cons 3 (helper (rest args)))])))
      (helper args))
    

    Alternatively, a named let could be used.