Front page | perl.perl6.language | Postings from February 2005

## Fun with junctions (was Sets vs Junctions)

From:
Patrick R. Michaud
Date:
February 11, 2005 15:15
Subject:
Fun with junctions (was Sets vs Junctions)
Message ID:
20050211192251.GA5632@pmichaud.com
```On Fri, Feb 11, 2005 at 12:54:39AM -0600, Rod Adams wrote:
> Damian writes:
> >Junctions have an associated boolean predicate that's preserved across
> >operations on the junction. Junctions also implicitly distribute
> >across operations, and rejunctify the results.
>
> My brain is having trouble fully grasping that. Let me attempt a paraphrase:
>
> Junctions exist to be tested for something.
> When a test is performed, the junction is evaluated in terms of that
> test. A "result junction" is created, which contains only the elements
> of the original junction which will pass that given test. If the result
> junction is empty, the test fails.

From "Perl 6 and Parrot Essentials":

A junction is basically just an unordered set with a logical
relation defined between its elements.  Any operation on the
junction is an operation on the entire set.

"Tests" are not used to select elements from the junction, a "test"
(such as a relational op) is simply applied to the elements of the
junction(s) and returns the junction of the results.  In other words,
if you think of a "test" as being a boolean operator, then applying
a boolean operator to a junction is going to return a junction of
true/false values, because boolean operators return true/false values.

For example, with the "less than or equals" (<=) relational operator,
the expression

any(2,3,4) <= 3

becomes

any( 2 <= 3,    # 1  (true)
3 <= 3,    # 1  (true)
4 <= 3     # 0  (false)
)

which ultimately becomes any(1,0), because <= is an operator that
returns booleans.  In plain English, we're asking "Are any of the values
2, 3, or 4 less than or equal to 3?", and the answer is "yes", because
any(1,0) evaluates to true in a boolean context.  Note that it does
*not* becomes the junction of the values that were less than or equal to 3
-- for that we would use C<grep>.

Similarly, consider

all(2,3,4) <= 3

which becomes

all( 2 <= 3,    # 1  (true)
3 <= 3,    # 1  (true)
4 <= 3     # 0  (false)
)

or all(1,0).  Here, the English question is "Are all of the values
2, 3, and 4 less than or equal to 3?", and the answer is "no" (and
all(1,0) evaluates to false in a boolean context).

Okay, so how is this useful?  Here's an example (w/apologies for any

if (any(@age) >= 100) { say "There's a centenarian here!"; }

is somehow a lot nicer than

if (grep { \$^x >= 100 } @age) { say "There's a centenarian here!"; }

And

if (all(@age) >= 100) { say "We're all centenarians here!"; }

is much nicer than

if (!(grep { \$^x < 100 } @age)) { say "We're all centenarians here!"; }

But wait, there's more!  Junctions are valuable because we can combine
them into multiple operands or function arguments.  Thus,

# intersection:  Are any values in @foo also in @bar?
any(@foo) == any(@bar)

# containment:  Are all of the elements in @foo also in @bar?
all(@foo) == any(@bar)

# non-intersection:  Are all of the elements in @foo not in @bar?
all(@foo) == none(@bar)

Here's that last one spelled out to see the effects, assuming @foo=(2,3,4)
and @bar=(5,6):

all(2,3,4) == none(5,6)

-> all( 2 == none(5,6),
3 == none(5,6),
4 == none(5,6)
)

-> all( none(2==5, 2==6),
none(3==5, 3==6),
none(4==5, 4==6)
)

-> all( none(0, 0),
none(0, 0),
none(0, 0)
)

Of course, the value of junctions is that they work pretty much
with any operation on scalar arguments.  Thus, if we define an
is_factor() function as:

# return true if \$x is a factor of \$y
sub is_factor (Scalar \$x, Scalar \$y) { \$y % \$x == 0 }

then we automatically get:

# are any of @foo factors of \$bar?
if is_factor(any(@foo), \$bar) { ... }

# are all of @foo factors of \$bar?
if is_factor(all(@foo), \$bar) { ... }

# is \$foo a factor of any elements of @bar?
if is_factor(\$foo, any(@bar)) { ... }

# is \$foo a factor of all elements of @bar?
if is_factor(\$foo, all(@bar)) { ... }

# are any elements of @foo factors of any elements of @bar?
if is_factor(any(@foo), any(@bar)) { ... }

# are all elements of @foo factors of all elements of @bar?
if is_factor(all(@foo), all(@bar)) { ... }

# a (somewhat inefficient?) is_prime test for \$bar
if is_factor(none(2..sqrt(\$bar)), \$bar) { say "\$bar is prime"; }

Just because I'm curious, here's the the prime test spelled out for
\$bar==23, testing if 23 is a prime number:

is_factor(none(2..sqrt(23)), 23)

-> is_factor(none(2..4), 23)

-> { 23 % none(2..4) == 0 }

-> { none( 23 % 2, 23 % 3, 23 % 4 ) == 0 }

-> { none( 1, 2, 3 ) == 0 }

-> { none( 1==0, 2==0, 3==0 ) }

-> { none( 0, 0, 0) }

-> true

That is just too cool.  :-)

Pm

```