[Chicago-talk] [PERL6]: Fun with junctions (was Sets vs Junctions)

Andy_Bach at wiwb.uscourts.gov Andy_Bach at wiwb.uscourts.gov
Mon Feb 14 13:01:08 PST 2005


I don't know if anybody *else* is on the edge of their seat, await P6 but 
... I ran into the "Perl 6 and Parrot Essentials" book at a used book 
store and so ended up on the P6 lists.  Its not your mother's perl 
anymore; well, it will be (it'll have full P5 compatability somehow), but 
its a whole lot more.  One new feature are junctions:
http://dev.perl.org/perl6/synopsis/S09.html

set-like, array like, yet w/ their own set of operations, like "any" and 
"all" ...  below is a nice summary of what junctions are and why we want 
them.

a

Andy Bach, Sys. Mangler
Internet: andy_bach at wiwb.uscourts.gov 
VOICE: (608) 261-5738  FAX 264-5932

"Bugs happen. A bug is a test case you haven't written yet."
Mark Pilgrim
----- Forwarded by Andy Bach/WIWB/07/USCOURTS on 02/14/2005 02:48 PM -----

"Patrick R. Michaud" <pmichaud at pobox.com> 
02/11/2005 01:22 PM

To
Rod Adams <rod at rodadams.net>
cc
"perl6-language at perl.org" <perl6-language at perl.org>
Subject
Fun with junctions (was Sets vs Junctions)






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
inadvertent syntax errors):

    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



More information about the Chicago-talk mailing list