Often in my coding labors I wish for alternative forms of certain infix operators, such as + or &&, that I could use with an arbitrary number of operands. This is no mere boast of monkly laziness! It's not just a matter of not wanting to write all those +s and &&s. There are times when the number of operands is not known until runtime... Well, come to think of it, it is still a case of monkly laziness.
A very risky kluge I have encountered in my readings of Perl code is this sort of thing:
or$sum = eval join ' + ', @addends;
Yikes. If @flags contains an undef the last expression "works" only because the eval fails with a syntax error. The counterpart using ' || ' can't even pretend to work.$all_true = eval join ' && ', @flags;
A better alternative is to go ahead and define
Simple, nice and symmetrical, and they do the right thing even for an empty @_:sub AND { ! grep !$_, @_ } sub OR { !!grep $_, @_ }
The fly in this ointment is that, unlike && and ||, AND and OR as defined above are not short-circuiting: every member of @_ is checked by grep, whether the answer requires it or not. It would be nice to have something like grep that returned only the first element of a list that passed a test.DB<1> p AND() 1 DB<2> p AND(0) DB<3> p AND(1) 1 DB<4> p AND(2, 3, 0, 1) DB<5> p OR() DB<6> p OR(1) 1 DB<7> p OR(2, 3, 0, 1) 1
As it happens, there is such a thing in List::Util. From the docs:
first BLOCK LISTOK, so we try
Similar to "grep" in that it evaluates BLOCK setting $_ to each element of LIST in turn. "first" returns the first element where the result from BLOCK is a true value. If BLOCK never returns true or LIST was empty then "undef" is returned.
This OR works fine:use List::Util 'first'; sub AND { ! first { !$_ } @_ } sub OR { !!first { $_ } @_ }
The AND, not so good:DB<1> p OR(0) DB<2> p OR() DB<3> p OR(1) 1 DB<4> p OR(undef, 2, 0, 'x') 1
The problem is that in this implementation of AND, the outer ! is no longer negating the answer to the question "how many falses did you find", but rather it's negating that first false found. But it's worse than that: first is inherently ambiguous with tests like { ! $_ } or { ! defined $_ }, because a return value of undef could either mean "I didn't find any" or "I found this one." Maybe first is not the way to go.DB<1> p AND(0) 1 DB<2> p AND() 1 DB<3> p AND(1) 1 DB<4> p AND(undef, 2, 0, 'x') 1
OK, let's try something slightly more verbose but more straightforward:
It is good. The gods of Perl have found pleasure in putting a little bit of || in AND, and a little bit of && in OR. A case of Perl ying-yang.sub AND { $_ || return 0 for @_; 1 } sub OR { $_ && return 1 for @_; 0 } __END__ DB<1> p AND(0) 0 DB<2> p AND() 1 DB<3> p AND(1) 1 DB<4> p AND(undef, 2, 0, 'x') 0 DB<5> p OR(0) 0 DB<6> p OR() 0 DB<7> p OR(1) 1 DB<8> p OR(undef, 2, 0, 'x') 1
But even though List::Util::first did not help with AND and OR, the very handy function List::Util::reduce can be used to generate multi-argument counterparts of other infix operators:
(The added 0 and 1 make sure that these behave righteously when @_ is empty.) List::Util::reduce is a truly handy little function, one that I wish had made to the Perl core; the docs have more examples of its usefulness.use List::Util 'reduce'; sub add { reduce { $a + $b } ( 0, @_ ) } sub mult { reduce { $a * $b } ( 1, @_ ) }
And while I'm praying to the gods of Perl for new admissions to the core, let me pray for multiarg counterparts for all those infix operators for which they would make sense.
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: An infix fix
by dragonchild (Archbishop) on Mar 22, 2005 at 14:11 UTC | |
by stvn (Monsignor) on Mar 22, 2005 at 18:02 UTC | |
by MidLifeXis (Monsignor) on Mar 22, 2005 at 18:29 UTC | |
by stvn (Monsignor) on Mar 22, 2005 at 20:21 UTC | |
by tlm (Prior) on Mar 22, 2005 at 14:35 UTC | |
by dragonchild (Archbishop) on Mar 22, 2005 at 14:46 UTC | |
by hsmyers (Canon) on Mar 26, 2005 at 18:46 UTC | |
|
Re: An infix fix
by ihb (Deacon) on Mar 22, 2005 at 14:45 UTC | |
|
Re: An infix fix
by Jenda (Abbot) on Mar 22, 2005 at 14:24 UTC | |
by Roy Johnson (Monsignor) on Mar 22, 2005 at 14:38 UTC | |
|
Re: An infix fix
by gaal (Parson) on Mar 22, 2005 at 14:15 UTC | |
by hardburn (Abbot) on Mar 22, 2005 at 20:16 UTC | |
|
Re: An infix fix
by Roy Johnson (Monsignor) on Mar 22, 2005 at 16:22 UTC | |
by tlm (Prior) on Mar 22, 2005 at 21:56 UTC | |
by Roy Johnson (Monsignor) on Mar 22, 2005 at 22:59 UTC | |
by tlm (Prior) on Mar 22, 2005 at 23:37 UTC | |
by Roy Johnson (Monsignor) on Mar 23, 2005 at 00:13 UTC | |
by tlm (Prior) on Mar 22, 2005 at 23:05 UTC | |
by Roy Johnson (Monsignor) on Mar 22, 2005 at 23:30 UTC | |
|
Re: An infix fix
by hardburn (Abbot) on Mar 22, 2005 at 20:15 UTC | |
by Jenda (Abbot) on Mar 22, 2005 at 23:18 UTC | |
by dragonchild (Archbishop) on Mar 23, 2005 at 14:03 UTC | |
by Jenda (Abbot) on Mar 24, 2005 at 14:49 UTC | |
by dragonchild (Archbishop) on Mar 24, 2005 at 15:03 UTC | |
by hardburn (Abbot) on Mar 23, 2005 at 14:09 UTC | |
by Anonymous Monk on Mar 23, 2005 at 03:25 UTC | |
by parv (Parson) on Mar 27, 2005 at 00:45 UTC | |
|
Re: An infix fix
by parv (Parson) on Mar 27, 2005 at 01:25 UTC |