Faking tcl::mathop::&& and ||

The set of tcl::mathop commands does not include commands corresponding to the && or || operators for reasons that are, well, reasonable. You can still (easily) fake them on lists of values that are a subset of the general set of boolean values, namely 0 (falsity) and all non-zero numerical values (truth). (You can also fake them on general boolean values that aren't numeric, such as yes or false, but not as easily or conveniently: I show commands that do just that at the bottom of the page.)

Limiting the boolean domain to this sub-domain is actually quite natural in Tcl. Any boolean expression already does evaluate to a value in this sub-domain, or can be trivially converted to a value in it by applying the logical negation (NOT, !) twice:

set foo [list true 1 [expr {5 > 2}]]
# => true 1 1
set bar [lmap elem $foo { expr {!!$elem} }]
# => 1 1 1

Because the definition is that 0 means false and non-zero means true, we can use tcl::mathop::* to perform a multi-argument AND operation on these values:

::tcl::mathop::* {*}$bar
# => 1
::tcl::mathop::* [expr {5 > 2}] [expr {5 > 4}] [expr {5 > 7}]
# => 0

since the semantics of multiplication correspond to the semantics of conjunction (AND) when the domain of boolean values is defined this way, i.e.:

multiplication
the product of a set of operands is non-zero if and only if every operand is non-zero
conjunction
the conjunction of a set of operands is non-zero if and only if every operand is non-zero

Even in the case where there are no arguments provided the semantics are a fit, since the multiplicative and the conjunctive identities are both 1.

An important caveat is that there is no short-circuit evaluation: every argument is evaluated.

Multi-argument OR can also be faked along similar lines:

::tcl::mathop::+ [expr {5 > 2}] [expr {5 > 4}] [expr {5 > 7}]
# => 2
# (and 2 is logically true!)
::tcl::mathop::+ [expr {5 > 12}] [expr {5 > 14}] [expr {5 > 7}]
# => 0

since the semantics of addition correspond to the semantics of inclusive disjunction (OR) when the domain of boolean values is defined this way, i.e.:

addition
the sum of a set of operands is zero if and only if every operand is zero
inclusive disjunction
the inclusive disjunction of a set of operands is zero if and only if every operand is zero

Even in the case where there are no arguments provided the semantics are a fit, since the additive and the disjunctive identities are both 0.

An important caveat is (as above) that there is no short-circuit evaluation: every argument is evaluated.Commands can be implemented for these operations in this way (but again, for every set of boolean values that aren't the boolean literals on, true, etc, the tcl::mathop::* and + commands are sufficient):

proc listAnd args {
    # Performs serial logical conjunction without short-circuit evaluation.
    # Every argument shall be a pre-evaluated boolean value.
    # If given at least one false value, returns 0, otherwise (including if given no values) returns 1.
    ::tcl::mathop::* {*}[lmap arg $args { expr {!!$arg} }]
}

proc listOr args {
    # Performs serial logical inclusive disjunction without short-circuit evaluation.
    # Every argument shall be a pre-evaluated boolean value.
    # If given at least one true value, returns 1, otherwise (including if given no values) returns 0.
    expr {[::tcl::mathop::+ {*}[lmap arg $args { expr {!!$arg} }]] > 0}
}

% listOr [expr {5 > 12}] [expr {5 > 14}] [expr {5 > 7}]
# => 0
% listOr [expr {5 > 12}] [expr {5 > 4}] [expr {5 > 7}]
# => 1
% listAnd [expr {5 > 2}] [expr {5 > 4}] [expr {5 > 7}]
# => 0
% listAnd [expr {5 > 2}] [expr {5 > 4}]
# => 1

See also