Parallel Assignment

January 11, 2019

Recently, I was writing a program where I needed parallel assignment; the details of my program don’t matter. Some languages provide parallel assignment natively; for instance, Python:

Python 2.7.13 (default, Mar 13 2017, 20:56:15)
[GCC 5.4.0] on cygwin
Type "help", "copyright", "credits" or "license" for more information.
>>> a,b,c = 1,2,3
>>> print a,b,c
1 2 3
>>> a,b,c = b,c,a
>>> print a,b,c
2 3 1

You can do something similar in C (I think — I’m not sufficiently expert in C to be certain this works):

#define SWAP(x, y) { typeof(x) t = x; x = y; y = t }

Your task is to add parallel assignment to your favorite programming language. When you are finished, you are welcome to read or run a suggested solution, or to post your own solution or discuss the exercise in the comments below.

Pages: 1 2

8 Responses to “Parallel Assignment”

  1. matthew said
    #include <stdio.h>
    
    template <typename T0, typename T1>
    void assign(T0 &t0, T1 &t1, T0 e0, T1 e1) {
      t0 = e0; t1 = e1;
    }
    
    template <typename T>
    void swap(T &t0, T &t1) {
      assign(t0,t1,t1,t0);
    }
    
    int main() {
      int a = 0, b = 1;
      printf("%d %d\n",a,b);
      swap(a,b);
      printf("%d %d\n",a,b);
      swap(a,b);
      printf("%d %d\n",a,b);
    }
    
  2. Lucio said

    The code in C in your email above does a swap of the values of x and y using t as temporary storage. C does not have parallel assignment

  3. matthew said

    Well, you can do something similar to my C++ solution with a macro in C (assuming some compiler extensions):

    #include <stdio.h>
     
    #define assign(t0,t1,e0,e1) \
      ({typeof(e0) _e0 = (e0); typeof(e0) _e1 = (e1); \
        (t0) = _e0; (t1) = _e1; \
      })
    
    #define swap(t0,t1) (assign(t0,t1,t1,t0))
     
    int main() {
      int a = 0, b = 1;
      printf("%d %d\n",a,b);
      swap(a,b);
      printf("%d %d\n",a,b);
      swap(a,b);
      printf("%d %d\n",a,b);
    }
    
  4. matthew said

    That should be “typeof(e1) _e1 = …” of course. And swap should be SWAP if we use the convention that macros that evaluate their arguments multiple times should be in upper case.

  5. chaw said

    I wrote this before reading the solution by @programmingpraxis
    (Dybvig’s pset!). It’s not as clever as that one but I think it works
    as needed.

    (import (scheme base)
            (scheme write))
    
    (define-syntax parallel-set!
      (syntax-rules ()
        ((_ () ()) ; just for completeness
         (values))
        ((_ (d0) (s0)) 
         (set! d0 s0))
        ((_ (d0 d1 d2 ...) (s0 s1 s2 ...))
         (let ((t0 s0))
           (parallel-set! (d1 d2 ...) (s1 s2 ...))
           (set! d0 t0)))))
    
    (let ((a 'va)
          (b 'vb)
          (c 'vc)
          (d 'vd)
          (e 've)
          (f 'vf))
      (parallel-set! (a b c d e f) (b c a e d f))
      (display (list a b c d e f))
      (newline))
    

    (vb vc va ve vd vf)
    

  6. Daniel said

    Here’s a solution in Common Lisp.

    (defun every-other (lst)
      (labels ((rec (x acc)
    	     (if (null x)
    		 acc
    		 (rec (cddr x) (cons (car x) acc)))))
        (reverse (rec lst (list)))))
    
    (defmacro parsetf (&rest pairs)
      (let* ((vars (every-other pairs))
    	 (vals (every-other (cdr pairs)))
    	 (syms (loop repeat (length vars) collect (gensym)))
    	 (env (mapcar #'list syms vals))
    	 (setfs (mapcar (lambda (x y) (list 'setf x y)) vars syms)))
        (if (/= (length vars) (length vals))
    	(error "too few values")
    	`(let* ,env
    	   (progn ,@setfs nil)))))
    

    Example Usage:

    CL-USER> (let ((a 1)
    	       (b 2)
    	       (c 3))
    	   (print (list a b c))
    	   (parsetf a b b c c a)
    	   (print (list a b c))
    	   nil)
    
    (1 2 3) 
    (2 3 1) 
    NIL
    
  7. Daniel said

    I didn’t realize the editor was using tabs. Here’s the same code, hopefully properly formatted (indented) this time.

    (defun every-other (lst)
      (labels ((rec (x acc)
                 (if (null x)
                     acc
                     (rec (cddr x) (cons (car x) acc)))))
        (reverse (rec lst (list)))))
    
    (defmacro parsetf (&rest pairs)
      (let* ((vars (every-other pairs))
             (vals (every-other (cdr pairs)))
             (syms (loop repeat (length vars) collect (gensym)))
             (env (mapcar #'list syms vals))
             (setfs (mapcar (lambda (x y) (list 'setf x y)) vars syms)))
        (if (/= (length vars) (length vals))
            (error "too few values")
            `(let* ,env
               (progn ,@setfs nil)))))
    

    Example usage:

    CL-USER> (let ((a 1)
                   (b 2)
                   (c 3))
               (print (list a b c))
               (parsetf a b b c c a)
               (print (list a b c))
               nil)
    
    (1 2 3) 
    (2 3 1) 
    NIL
    
  8. Richard A. O'Keefe said

    Common Lisp already has PSETQ and PSETF.
    (psetq a b b a) ; swap a and b
    (psetf (aref v i) (aref v j) (aref v j) (aref v i)) ; swap a[i] and a[j]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: