21

Suppose I have

(setq a 1 b 2)

How can I elegantly swap the values of a and b without using a temporary variable?

PythonNut
  • 10,243
  • 2
  • 29
  • 75
  • While I remember the swap operation from programming examples many many years ago, I don't think I've ever needed such a "swap" operation. So where do you find you need such a thing? – Stefan Aug 20 '15 at 19:55
  • @Stefan this time, I'm writing a function that takes two arguments, and I'd like to ensure that the first argument is the smaller of the two. – PythonNut Aug 20 '15 at 19:57
  • 1
    @PythonNut, well you can bind first argument to `(min a b)` and second to `(max a b)`. This is one solution. Some will argue that this requires two comparisons when one suffices, that's right. You can handle it with one comparison in more functional manner still, for example using destructuring bind `(cl-destructuring-bind (a . b) (if (< a b) (cons a b) (cons b a)) ...)`. This is another way. – Mark Karpov Aug 20 '15 at 19:59
  • 1
    @Mark true, but, at least to me, that feels like swatting flies with hand grenades. `cl-destructuring-bind` is a ridiculously powerful tool for this job. – PythonNut Aug 20 '15 at 22:54

3 Answers3

21

This is the elegant idiom I use ;-).

(setq a  (prog1 b (setq b  a)))
PythonNut
  • 10,243
  • 2
  • 29
  • 75
Drew
  • 75,699
  • 9
  • 109
  • 225
18

If memory serves me well and you're willing to use cl-lib then:

(cl-rotatef a b)

Note that this is Common Lisp way of solving the problem.

Mark Karpov
  • 4,893
  • 1
  • 24
  • 53
6

If it's integers:

(setq a (logxor a b))
(setq b (logxor a b))
(setq a (logxor a b))

:)

jtgd
  • 944
  • 4
  • 13
  • 4
    For completeness you should also include the following classic: a = a + b, b = a - b, a = a - b. Translated to Emacs Lisp, of course :-D – Mark Karpov Aug 21 '15 at 09:15
  • 1
    True, and for completeness I'll point out that in asm or C the The XOR Trick works for anything; registers, memory, ints, floats, structs, strings(equal length)... In Lisp I think only ints. For large blocks of memory it's nice to not need the temp buffer. – jtgd Aug 21 '15 at 20:07
  • @jtgd: For large blocks of memory, you can do the swap segment-by-segment, with a small buffer. – Clément Jul 27 '16 at 15:34
  • @MarkKarpov: `(let* ( (a (+ a b)) (b (- a b)) (a (- a b)) ) … )` – hackerb9 Aug 12 '23 at 03:44