╔═╗╔╗╔╔╦╗╔═╗╦═╗╔═╗╦═╗╦╔═╗╔═╗ ║╣ ║║║ ║ ║╣ ╠╦╝╠═╝╠╦╝║╚═╗║╣ ╚═╝╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╩╚═╝╚═╝ ╔═╗╔═╗╔═╗╦═╗╔═╗╔╦╗╔═╗╦═╗ ║ ║╠═╝║╣ ╠╦╝╠═╣ ║ ║ ║╠╦╝ ╚═╝╩ ╚═╝╩╚═╩ ╩ ╩ ╚═╝╩╚═ ============================================================================ Conditional list elements. You want an item in a list only if some condition is true: ()x!! The Enterprise operator. It boldly goes where no ternary has gone before. Named because it looks like the starship: ()x!! ( ) saucer section x engineering hull !! nacelles Engage. ============================================================================ PART 1: THE PROBLEM ------------------- You're building a list. Some elements are conditional: my @args = ( '--verbose', ($debug ? '--debug' : ()), '--output', $file, ); That ternary is ugly. And it gets uglier with more conditions. ============================================================================ PART 2: THE SOLUTION -------------------- my @args = ( '--verbose', ('--debug') x !!$debug, '--output', $file, ); If $debug is true, you get '--debug'. If $debug is false, you get nothing. Clean. Readable. Logical. ============================================================================ PART 3: HOW IT WORKS -------------------- Break it down: ('--debug') x !!$debug PART MEANING ------------ ------------------------------------------ ('--debug') A list containing one string x List repetition operator !!$debug Boolean: 1 if true, '' (0) if false When $debug is true: ('--debug') x 1 # Repeat once: ('--debug') When $debug is false: ('--debug') x 0 # Repeat zero times: () Zero repetitions = empty list = element disappears. .--. |o_o | |:_/ | // \ \ (| | ) /'\_ _/`\ \___)=(___/ ============================================================================ PART 4: THE BANG BANG --------------------- Why !!$debug instead of just $debug? The x operator needs a number on the right. Boolean values in Perl: True: 1 False: '' (empty string) But '' in numeric context is 0. So it works without !!... usually. The !! makes it explicit. Self-documenting. And avoids edge cases where the condition might return something weird. ============================================================================ PART 5: MULTIPLE CONDITIONS --------------------------- Stack them up: my @cmd = ( 'rsync', ('-v') x !!$verbose, ('-n') x !!$dry_run, ('-z') x !!$compress, ('--delete') x !!$delete, $source, $dest, ); Each flag appears only if its condition is true. No nested ternaries. No ugly if blocks building up @cmd. ============================================================================ PART 6: MULTIPLE VALUES ----------------------- Works with multiple elements too: my @opts = ( ('--user', $user) x !!$user, ('--pass', $pass) x !!$pass, ); If $user is set, you get ('--user', $user). If not, you get nothing. Both elements appear or neither does. ============================================================================ PART 7: HASH INITIALIZATION --------------------------- Works in hash construction: my %config = ( name => $name, (debug => 1) x !!$DEBUG, (verbose => 1) x !!$VERBOSE, ); Conditional hash entries without ternary mess. ============================================================================ PART 8: FUNCTION CALLS ---------------------- Pass optional arguments: some_function( $required_arg, ('optional' => $value) x !!defined($value), ); The optional parameter only appears when defined. ============================================================================ PART 9: VS TERNARY ------------------ Compare: # Ternary approach my @list = ( 'always', ($cond ? 'maybe' : ()), ($other ? ('a', 'b') : ()), ); # Enterprise approach my @list = ( 'always', ('maybe') x !!$cond, ('a', 'b') x !!$other, ); Enterprise is: - Shorter - More consistent - Easier to read in long lists ============================================================================ PART 10: GOTCHAS ---------------- Parentheses matter: 'item' x !!$cond # Wrong: 'item' x 1 = 'item' (scalar) ('item') x !!$cond # Right: ('item') x 1 = ('item') (list) Without parens, x does string repetition, not list repetition. Also watch for precedence: ('a', 'b') x !!$cond && $other # Wrong: !! binds to $cond only ('a', 'b') x !!($cond && $other) # Right: condition grouped ============================================================================ PART 11: REAL WORLD EXAMPLE --------------------------- Building a database query: my @clauses = ( "SELECT * FROM users", ("WHERE active = 1") x !!$active_only, ("AND role = ?") x !!$role, ("ORDER BY $sort") x !!$sort, ("LIMIT $limit") x !!$limit, ); my $sql = join ' ', @clauses; Clean SQL construction with optional clauses. ============================================================================ PART 12: COMMAND LINE BUILDER ----------------------------- my @ffmpeg = ( 'ffmpeg', ('-y') x !!$overwrite, '-i', $input, ('-c:v', $vcodec) x !!$vcodec, ('-c:a', $acodec) x !!$acodec, ('-b:v', $bitrate) x !!$bitrate, ('-ss', $start) x !!$start, ('-t', $duration) x !!$duration, $output, ); system @ffmpeg; All the complexity hidden in clean, scannable code. ============================================================================ PART 13: THE NAME ----------------- Enterprise. As in USS Enterprise. The starship. ()x!! ( ) Saucer section (primary hull) x Engineering section (secondary hull) !! Warp nacelles ()x!! ___ / \ ( ) \___/ | x /|\ / | \ !! | !! Okay, you need imagination. But Perl operators have whimsical names, and this one stuck because it's memorable. Also called the "conditional list element operator" if you want to be boring about it. ============================================================================ ()x!! _______ / \ ( () ) \_______/ | x /|\ / | \ !! | !! | ENGAGE Conditional elements at warp speed ============================================================================ japh.codes