Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple bindings in a for loop

So, the Racket (6.5) documentation says you can bind several ids at once:

(for ([(i j) #hash(("a" . 1) ("b" . 20))])
    (display (list i j)))

Bu-u-ut I can't figure out / find an example of how to do this with manually constructed data:

(define a '(1 2 3 4 5))
(define b '(10 20 30 40 50))
(for ([(i j) (map list a b)])
    (display (list i j)))

explodes with

result arity mismatch;
 expected number of values not received
  expected: 2
  received: 1
  from: 
  in: local-binding form
  values...:

What am I missing?

like image 539
vpozdyayev Avatar asked Dec 15 '22 06:12

vpozdyayev


2 Answers

This

(for ([(i j) #hash(("a" . 1) ("b" . 20))])
    (display (list i j)))

is short for

(for ([(i j) (in-hash #hash(("a" . 1) ("b" . 20)))])
    (display (list i j)))

Now in-hash returns two values at a time, so (i j) will be bound to the two values.

On the other hand, this:

(for ([(i j) (map list a b)])
    (display (list i j)))

is short for

(for ([(i j) (in-list (map list a b))])
    (display (list i j)))

and in-list will return one element at a time (in you example the element is a list). Since there are two names in (i j) and not just one, an error is signaled.

Follow Toxaris' advice in in-parallel.

UPDATE

The following helper make-values-sequence shows how to create a custom sequence, that repeatedly produces more than one value.

#lang racket

(define (make-values-sequence xss)
  ; xss is a list of (list x ...)
  (make-do-sequence (λ ()
                      (values (λ (xss) (apply values (first xss))) ;  pos->element
                              rest                                 ;  next-position
                              xss                                  ;  initial pos
                              (λ (xss) (not (empty? xss)))         ;  continue-with-pos?
                              #f                                   ;  not used
                              #f))))                               ;  not used]


(for/list ([(i j) (make-values-sequence '((1 2) (4 5) (5 6)))])
  (+ i j))

Output:

'(3 9 11)
like image 105
soegaard Avatar answered Feb 28 '23 09:02

soegaard


In this example, you can use separate clauses to bind i and j:

(for ([i (list 1 2 3 4 5)]
      [j (list 10 20 30 40 50)])
  (display (list i j)))

More generally, you can use in-parallel to create a single sequence of multiple values from multiple sequences of single values:

(for ([(i j) (in-parallel (list 1 2 3 4 5)
                          (list 10 20 30 40 50))])
    (display (list i j)))

Both solutions print (1 10)(2 20)(3 30)(4 40)(5 50).

like image 43
Toxaris Avatar answered Feb 28 '23 10:02

Toxaris