Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I handle a variable number of arguments passed to a function in Racket?

I like creating functions which take an unlimited number of arguments, and being able to deal with them as a list. It's been useful to me when creating binary trees & I'm using it for a variation on the nearest-neighbor algorithm right now. My method, however, is really horrible: since I can't think of a way to iterate over an improper list (which may well be improper & degenerate), I tried using various list functions to force the improper list into list form.

This is my best attempt in a simple function to determine difference between map-nodes (works, just not sure why it works):

(define distance-between
  (lambda xs
    (let ([input-list (list* xs null)])
      (letrec 
          ([f (lambda (xs acc)
               (if (null? (cdr xs))
                   acc
                   (f (cdr xs) 
                      (+ (abs (- (map-node-x (car xs)) 
                                 (map-node-x (cadr xs))))
                         (abs (- (map-node-y (car xs)) 
                                 (map-node-y (cadr xs))))
                         acc))))])                   
       (f (car input-list) 0)))))

As you can see, it's an ugly solution and involves some of what seems like magic to me - why is the improper list coerced into list form when I include it in a list*? (note: this sentence is misleading, this does not occur).

I'd rather have a pretty solution and no magic. Can anyone help?

For example a typical input would be:

(distance-between (map-node 1 2) (map-node 2 3) (map-node 3 4))

with the expected result:

4

(a distance of 2 between map-node (a) and m-n (b), plus a distance of 2 between map-node (b) and map-node (c)).

Alternatively one might simply input:

(distance-between (map-node 1 2) (map-node 2 2))

and get an answer of:

1

If I attempted this on the raw input, without my (let ([input-list...])...) statement, it would cause an error as (? not actually sure why given response to this question).

The function works as expected.

like image 754
James Duval Avatar asked Dec 06 '22 06:12

James Duval


1 Answers

There's nothing improper about the list received as a variadic argument list (meaning: variable number of arguments). For example:

(define test-list
  (lambda xs
    (length xs))) ; xs is a normal list, use it like any other list

(test-list 1 2 3 4)
=> 4

In the above example, the xs parameter is a normal, plain, vanilla list, there's nothing improper about it. You can iterate over it as you would over any other list. There's no need to car it, it's already a list! Also, notice that the same function can be written like this:

(define (test-list . xs)
  (length xs))   ; xs is a normal list, use it like any other list

Just for reference: an improper list is one that does not end with the null list. For example: '(1 2 3 . 4). Again, that's not how a variadic argument list looks.

like image 198
Óscar López Avatar answered Jan 17 '23 02:01

Óscar López