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.
#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); }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
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); }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.
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))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:
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) NILCommon 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]