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

I like to create functions that take an unlimited number of arguments and can deal with them in a list. This was useful for me when creating binary trees, and I'm using it to change the nearest neighbor algorithm right now. My method, however, is really terrible: since I cannot think of a way to iterate over the wrong list (which may well be wrong and degenerate), I tried to use various functions of the list to bring the wrong list into list form.

This is my best attempt at a simple function to determine the difference between map-node (works, just not sure why this 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, this is an ugly solution and includes some of what seems magical to me - why is the wrong list forced to enter the list form when I include it in the list *? (note: this sentence is misleading, this does not happen).

I would prefer a beautiful solution and not magic. Can anyone help?

For example, a typical input:

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

with expected result:

 4 

(distance 2 between the maps node (a) and mn (b), as well as the distance 2 between the maps node (b) and map-node (c)).

Alternatively, you can simply enter:

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

and get the answer:

 1 

If I tried to do this on raw input, without mine (let ([input-list ...]) ...), it will throw an error like (? Not quite sure why this answer is to this question).

The function is working properly.

+4
source share
3 answers

There is nothing wrong with the list obtained as a list of variational arguments (value: variable number of arguments). For instance:

 (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 regular, equal, vanilla list, there is nothing wrong with it. You can iterate over it, as on any other list. There is no need for a car , this is a list! Also note that the same function can be written as follows:

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

For reference only: an invalid list is one that does not end with a null list. For example: '(1 2 3 . 4) . Again, this is not what the list of variational arguments looks like.

+8
source

I also don't understand how your variational argument list might be wrong.

However, in order to answer your initial question (how to iterate over the possibly wrong list, somewhat more elegantly), here is one way: match :

 #lang racket (define (properly-sum-improper-list xs) (let loop ([acc 0] [xs xs]) (match xs [(list) acc] [(cons x more) (loop (+ acc x) more)] [x (+ acc x)]))) ;last item of improper list (require rackunit) (check-equal? (properly-sum-improper-list '(1 2 3 4)) 10) (check-equal? (properly-sum-improper-list '(1 2 3 . 4)) 10) 

However, if necessary, perhaps this is an indication that you want to correct or change something else.

+4
source

Your list is not incorrect. When your argument is not a pair, for example (lambda xs body ...) or (define (fun . xs) body ...) , all your arguments fall into the list. For example, (fun 1 2 3) will do xs '(1 2 3) . Doing (list* '(1 2 3) '()) does '((1 2 3) , which you cancel by invoking your loop with car , which does it again '(1 2 3) .

In addition, your procedure works as intended. You can clear your procedure a bit, but since there are no lists that slide above the list that folds over the next two items, it won't get much smaller. Below is basically the same code, but the procedure that does the work (which if there was a grouped pair that you could use) and with named let as an iterator loop (which is syntactic sugar for letrec +) is abstracted, is abstracted.

 (define (distance-between e1 . lst) (define (add-diff-acc e1 e2 acc) (+ (abs (- (map-node-x e1) (map-node-x e2))) (abs (- (map-node-y e1) (map-node-y e2))) acc)) (let iterate ((e1 e1) (lst lst) (acc 0)) (if (pair? lst) (let ((e2 (car lst))) (iterate e2 (cdr lst) (add-diff-acc e1 e2 acc))) acc))) 

EDIT: About sugar syntax named let and letrec .

 (let ((x 10) (y 19)) body) 

is syntactic sugar for anonymous procedure call

 ((lambda (xy) body) 10 19) 

A named let just gives this procedure a name, albeit with letrec , as it were, doing a recursive binding. you call it with the name you give, and the arguments will be what you supply instead of the initial value in let. I am used to them and prefer them today. It may take some time to get used to.

Most of the code we write is syntactic sugar for some lower-level materials. Macros are nested so that your letrec form can be reduced in the end lambdas. The whole procedure without syntactic sugar will look like this:

 (define distance-between (lambda (e1 . lst) ((lambda (add-diff-acc) ((lambda (iterate e1 lst acc) ; emulate Y to substitute `letrec` (iterate iterate e1 lst acc)) (lambda (iterate e1 lst acc) (if (pair? lst) ((lambda (e2) (iterate iterate e2 (cdr lst) (add-diff-acc e1 e2 acc))) (car lst)) acc)) e1 lst 0)) (lambda (e1 e2 acc) (+ (abs (- (map-node-x e1) (map-node-x e2))) (abs (- (map-node-y e1) (map-node-y e2))) acc))))) 
+3
source

Source: https://habr.com/ru/post/1496816/


All Articles