perl.gg / hidden-gems

Temp-Free Variable Swap

2026-03-08

Swap two variables. Every CS textbook teaches you to use a temporary:
my $temp = $x; $x = $y; $y = $temp;
Three lines. One throwaway variable. The kind of code that makes you feel like you're filling out paperwork.

Perl does it in one:

($x, $y) = ($y, $x);
No temp. No fuss. Just a clean swap. And it's not a hack or a trick. It's how list assignment actually works.

Part 1: WHY THIS WORKS

The key is evaluation order. Perl evaluates the entire right-hand side before it assigns anything to the left.
($x, $y) = ($y, $x);
Here's what happens step by step:
STEP WHAT HAPPENS ---- ----------------------------------------- 1 Evaluate ($y, $x) - builds a temporary list 2 That list contains the OLD values of $y and $x 3 Assign that list to ($x, $y) 4 $x gets the old $y, $y gets the old $x
The right side is fully resolved into a list of values before the left side sees anything. By the time assignment happens, the original values are safely captured in that intermediate list.

Perl builds the list, then assigns. Two phases, one statement.

Part 2: THE C PROGRAMMER'S PAIN

In C, you have no choice:
int temp = x; x = y; y = temp;
Python lets you do x, y = y, x for the same reason as Perl. But many languages force the temp variable dance.

Some clever folks try the XOR trick:

x ^= y; y ^= x; x ^= y;
Three operations, no temp, and completely unreadable. It also fails if x and y point to the same memory location. Clever, broken, and ugly. The trifecta.

Perl's list assignment is none of those things. It's how the language was designed to work.

Part 3: THREE OR MORE

The pattern scales. Rotating three values:
($a, $b, $c) = ($b, $c, $a);
Before:
$a = 1, $b = 2, $c = 3
After:
$a = 2, $b = 3, $c = 1
A left rotation in one line. Every value moves one position left, and the leftmost wraps around to the right.

Right rotation? Reverse the direction:

($a, $b, $c) = ($c, $a, $b);
You can do this with any number of variables. Four, five, ten. The principle holds. The entire right side is evaluated first, always.

Part 4: PRACTICAL USES

Sorting two values so the smaller comes first:
($min, $max) = ($max, $min) if $min > $max;
One line. Conditional swap. Clean. The statement modifier form reads almost like English: "swap them if they're in the wrong order."

Swapping hash values:

@hash{qw(first last)} = @hash{qw(last first)};
That's a hash slice on both sides. Same evaluation rule applies. The right side resolves fully before the left side gets touched.

Normalizing coordinates so (x1,y1) is always the top-left corner of a bounding box:

($x1, $x2) = ($x2, $x1) if $x1 > $x2; ($y1, $y2) = ($y2, $y1) if $y1 > $y2;
Two clean swaps. No temps cluttering up the namespace.

Toggling between two states:

my ($on, $off) = ("active", "inactive"); # Toggle current state ($on, $off) = ($off, $on); say "Current: $on"; # Current: inactive
Every swap is a toggle. Every toggle is a swap. Same mechanism.

Part 5: DECONSTRUCTING THE MAGIC

Let's prove this isn't just syntactic sugar. Consider what would happen if Perl assigned left-to-right simultaneously:
# HYPOTHETICAL broken behavior (not how Perl works!) ($x, $y) = ($y, $x); # If it assigned $x = $y first... # then $y = $x would use the NEW $x (which is already $y) # Result: both variables equal $y. Broken.
But Perl doesn't do that. The right-hand list is materialized as a snapshot. The values are frozen before assignment begins.

You can verify this yourself:

my $x = "alpha"; my $y = "omega"; ($x, $y) = ($y, $x); say "x = $x"; # x = omega say "y = $y"; # y = alpha
Works every time. The snapshot guarantees it.

Part 6: ARRAY ELEMENT SWAPS

Same trick works with array elements:
@arr[0, -1] = @arr[-1, 0];
That swaps the first and last elements. Array slices follow the same rules as list assignment.

Reversing the first and last three elements of an array:

@arr[0, 1, 2, -3, -2, -1] = @arr[-3, -2, -1, 0, 1, 2];
Getting fancy? Sure. But it works, and it's clear about what it does.

For adjacent swaps in a loop (bubble sort core):

for my $i (0 .. $#arr - 1) { if ($arr[$i] > $arr[$i + 1]) { @arr[$i, $i + 1] = @arr[$i + 1, $i]; } }
No temp variable in sight. The list assignment handles it.

Part 7: STRING TRICKS

Swap words in a string using capture groups and list assignment:
my $str = "hello world"; ($str) = $str =~ s~(\w+)(\s+)(\w+)~$3$2$1~r;
Okay, that's a regex approach. But here's a cleaner list assignment version:
my ($first, $second) = split /\s+/, $str; ($first, $second) = ($second, $first); $str = "$first $second";
Or even shorter, rebuild in one shot:
$str = join ' ', reverse split /\s+/, $str;
Different tool, same spirit. Perl gives you options.

Swapping keys and values in a hash? List assignment makes that a one-liner too:

my %original = (a => 1, b => 2, c => 3); my %reversed = reverse %original; # %reversed is (1 => 'a', 2 => 'b', 3 => 'c')
The reverse function flips the list, and hash assignment pairs them back up. Not exactly a swap in the variable sense, but the same evaluation principle at work. The entire list is built before the hash consumes it.

Part 8: THE FIBONACCI CONNECTION

The Fibonacci sequence is a natural fit for temp-free swapping:
my ($a, $b) = (0, 1); for (1 .. 20) { say $a; ($a, $b) = ($b, $a + $b); }
Each iteration, $b becomes the sum of the old $a and $b, while $a becomes the old $b. No temp needed because the right side evaluates completely before assignment.

In most languages, you'd need:

temp = a a = b b = temp + b
Perl's version reads like the mathematical definition. Two values, one transformation, one line.
fib(n): ($a, $b) = ($b, $a + $b) 0 1 1 2 3 5 8 13 21 34 \/ \/ \/ \/ \/ \/ \/ \/ /\ /\ /\ /\ /\ /\ /\ /\ 1 1 2 3 5 8 13 21 34 55

Part 9: MULTIPLE RETURN VALUES

Perl subroutines return lists. List assignment makes consuming them natural:
sub divide { my ($a, $b) = @_; return (int($a / $b), $a % $b); } my ($quotient, $remainder) = divide(17, 5); say "17 / 5 = $quotient remainder $remainder";
And if you want to swap the return order? Just swap on the receiving end:
my ($remainder, $quotient) = reverse divide(17, 5);
The list is the native data structure in Perl. Assignment to lists is how Perl thinks. The temp-free swap isn't a special case. It's just list assignment doing what list assignment does.

Part 10: UNDER THE HOOD

What does Perl actually do internally? When the interpreter sees a list assignment, it:
1. Evaluates every element on the right side 2. Stores them in a temporary internal list (on the stack) 3. Assigns from that list to the left side, one element at a time
So there IS a temporary. It's just not YOUR temporary. Perl manages it internally on the stack. You don't name it, you don't declare it, and you don't clean it up. The interpreter handles the bookkeeping.

This is the same reason you can safely do things like:

@array = reverse @array;
Perl doesn't reverse in place and clobber itself. It builds the reversed list first, then assigns. The old @array contents survive long enough for the right side to finish.

Understanding this distinction matters. You're not avoiding temporary storage. You're letting Perl handle it for you instead of doing it manually. That's not laziness. That's abstraction.

Part 11: THE TAKEAWAY

The temp-free swap isn't a golf trick. It's not obfuscation. It's idiomatic Perl.

When you write ($x, $y) = ($y, $x), you're communicating intent more clearly than three lines with a temp variable. Anyone reading your code can see "these two values are being swapped" in a single glance.

The rule is simple: the right side is fully evaluated before the left side is assigned. That's it. That's the whole mechanism. Once you internalize it, you'll never reach for a temp variable again.

+---------+ | $temp | +---------+ | [VACANT] | "Position eliminated. Perl doesn't need you." .--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/

Created By: Wildcard Wizard. Copyright 2026