╔═╗╔═╗╔╦╗╔═╗ ╦ ╔═╗╔═╗╔═╗╔═╗ ║ ╦║ ║ ║ ║ ║ ║ ║ ║║ ║╠═╝╚═╗ ╚═╝╚═╝ ╩ ╚═╝ ╩═╝╚═╝╚═╝╩ ╚═╝ ============================================================================ goto. The forbidden keyword. The thing your CS professor warned you about. The fast track to spaghetti code. But what if you stuck it inside an else block? What if you made it loop? #!/usr/bin/env perl use feature qw|say|; my $n = 7; if ($n == 10) { say qq|I see a ${n}!|; } else { R: say qq(Hmmm... looking for 10... \$n = ${\$n++}); sleep 1; $n > 10 ? say qq(Yay! It's a ${\--$n}!) : goto R; } Run it. Watch it count from 7 to 10, then celebrate. No while. No for. Just goto doing things goto shouldn't do. ============================================================================ PART 1: THE SETUP ----------------- my $n = 7; We're starting at 7. The script wants to find 10. The if statement checks if we're already there: if ($n == 10) { say qq|I see a ${n}!|; } We're not at 10. So we fall into the else block. And that's where things get weird. ============================================================================ PART 2: THE LABEL ----------------- else { R: See that R: sitting right after the opening brace? That's a label. Labels are landing pads for goto. You can put them almost anywhere. Inside an else block? Sure, why not. Perl doesn't care. The label name is arbitrary. Could be LOOP: or AGAIN: or XYZZY: - Perl just needs something to jump to. ============================================================================ PART 3: THE INTERPOLATION TRICK ------------------------------- say qq(Hmmm... looking for 10... \$n = ${\$n++}); This line does two things at once. Let's dissect it: PIECE WHAT IT DOES ---------------- ------------------------------------------ qq(...) Double-quoted string (interpolates) \$n = Literal "$n =" (backslash escapes the $) ${\$n++} The magic part That last bit is dense: ${ ... } Dereference whatever's inside \$n++ Reference to $n, then post-increment So we: 1. Take a reference to $n 2. Dereference it (getting current value) 3. Post-increment $n The print shows the CURRENT value, then $n goes up by one. First iteration: prints 7, $n becomes 8 Second iteration: prints 8, $n becomes 9 Third iteration: prints 9, $n becomes 10 Fourth iteration: prints 10, $n becomes 11 Wait, 11? Yeah. We overshoot. Keep reading. ============================================================================ PART 4: THE DRAMATIC PAUSE -------------------------- sleep 1; One second between iterations. So you can watch the countdown. Remove it if you're impatient. ============================================================================ PART 5: THE CONDITIONAL GOTO ---------------------------- $n > 10 ? say qq(Yay! It's a ${\--$n}!) : goto R; A ternary operator deciding whether to celebrate or loop. CONDITION THEN ELSE ----------- ------------------------------ -------- $n > 10 Print success message goto R If $n is greater than 10, we're done. Print and exit. If not, jump back to label R and do it all again. But notice the success message: say qq(Yay! It's a ${\--$n}!) That's a PRE-decrement. $n was 11, becomes 10, we print 10. The overshoot was intentional. We needed to go past 10 to trigger the exit condition, then back up one for the correct answer. .--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/ ============================================================================ PART 6: THE FULL WALKTHROUGH ---------------------------- ITER $n START PRINTS $n END ACTION ---- -------- ------------------ ------ ----------- 1 7 "... $n = 7" 8 goto R 2 8 "... $n = 8" 9 goto R 3 9 "... $n = 9" 10 goto R 4 10 "... $n = 10" 11 $n > 10! - 11 "Yay! It's a 10!" 10 EXIT Four iterations through the else block. The goto creates a loop without any loop keywords. ============================================================================ PART 7: WHY THIS WORKS ---------------------- Labels in Perl are just markers. They don't create scope. They don't affect control flow by themselves. They just sit there, waiting. goto jumps to a label unconditionally. Doesn't matter where the label is - same block, different block, inside a conditional. If Perl can see it, goto can reach it. Putting a label at the start of an else block turns that block into a loop body. The goto at the end becomes the "continue" logic. It's a while loop in disguise: # This goto version: else { R: do_stuff(); condition ? exit : goto R; } # Is equivalent to: else { while (!condition) { do_stuff(); } } Same behavior. Different syntax. One will get you fired. ============================================================================ PART 8: THE ANNOTATED VERSION ----------------------------- #!/usr/bin/env perl use feature qw|say|; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # Starting value # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ my $n = 7; # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # The fake "loop" # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if ($n == 10) { # Already at target - just print say qq|I see a ${n}!|; } else { R: # <-- Label: goto target # Print current value, THEN increment # ${\$n++} dereferences and post-increments say qq(Hmmm... looking for 10... \$n = ${\$n++}); # Dramatic pause sleep 1; # Exit condition with pre-decrement to correct overshoot # If not > 10, jump back to R and repeat $n > 10 ? say qq(Yay! It's a ${\--$n}!) : goto R; } ============================================================================ PART 9: GOTO FLAVORS -------------------- Perl actually has three kinds of goto: goto LABEL Jump to a label (what we used) goto &NAME Replace current sub with another (tail call) goto EXPR Computed goto (jump to dynamic label) The label form is the "classic" goto that everyone warns about. The &NAME form is actually useful - it's a proper tail call that doesn't grow the call stack. Some recursive algorithms need it. The EXPR form is terrifying. You can compute where to jump at runtime. Don't. ============================================================================ PART 10: WHEN GOTO ISN'T EVIL ----------------------------- "Never use goto" is too strong. There are legitimate uses: * Breaking out of deeply nested loops (though labeled loops work) * Tail call optimization with goto &subname * Generated code (parsers, state machines) * Cleanup sections (C-style error handling) But using goto to build a loop inside an else block? That's showing off. Save it for code golf and late-night Perl hacking sessions. The real lesson here isn't "use goto." It's "Perl lets you put labels anywhere, and that's both powerful and dangerous." ============================================================================ _____ / \ | GOTO | | ---> | \_____/ | | ___V___ / \ | LABEL: | \_______/ "The forbidden jump" - use responsibly ============================================================================ japh.codes