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

## Re: SEND + MORE = MONEY (works now in pugs with junctions!)

From:
Date:
March 11, 2005 21:56
Subject:
Re: SEND + MORE = MONEY (works now in pugs with junctions!)
Message ID:
```Sam Vilain wrote:

>
> I've changed examples/sendmoremoney.p6 in the pugs distribution to use
> junctions correctly to demonstrate that they *can* be used to solve these
> sorts of problems, and that it is just a matter of semantics and writing
> code correctly.
>
> However, poor semantics can make the task of writing optimisers
> unnecessarily difficult or impractical, as Bourne demonstrated.
>
> in short, it seems people want this:
>
>   my \$a = any(1..9);
>   my \$b = any(1..9);
>   my \$c = any(0..9);
>
>   if ( \$a != \$b && \$b != \$c && \$a != \$c &&
>        (\$a + \$b == \$a * 10 + \$c) ) {
>       print "\$a + \$b = \$a\$c";
>   }
>
> To mean this (forgive the duplication of collapse() here):
>
>   sub collapse(\$x, \$sub) { \$sub.(\$x) }
>   sub collapse(\$x, \$y, \$sub) { \$sub.(\$x,\$y) }
>
>   my \$a = any(1..9);
>   my \$b = any(1..9);
>   my \$c = any(0..9);
>
>   collapse(\$a, \$b, -> \$a, \$b {
>       (\$a != \$b) &&
>       collapse(\$c, -> \$c {
>           if ( ( \$b != \$c ) && ( \$a != \$c ) &&
>                (\$a + \$b == \$a * 10 + \$c) ) {
>               say "\$a + \$b = \$a\$c";
>           }
>       });
>   });
>
> (and YES THAT WORKS <g>).

Yes, it does work, given the current state of autothreading.
Well, you'd need a C< use junctions; > in order to store a junction in a
variable. And I'm not certain if that changes the autothreading behavior
of the closures or not. You might have to declare the collapses as:

multi sub collapse (\$x is none(Junction), \$sub) {...};
multi sub collapse (\$x is none(Junction), \$y is none(Junction),
\$sub) {...};

to get it to thread correctly. I'm not sure. (Pretty sure you'd need the
'multi' in there).

It's also a perfect example of where my hyperthreader operator (it
hasn't been nailed down yet what the exact syntax is (or even if it
exists), I'm waiting for Damian to get back to continue that thread.) is
a better tool than junctions. Assuming we use my »« syntax,
SEND+MORE==MONEY could be written as:

sub test (\$s, \$e, \$n, \$d, \$m, \$o, \$r, \$y) {
if "\$s\$e\$n\$d" + "\$m\$o\$r\$e" == "\$m\$o\$n\$e\$y" &&
all(\$s,\$e,\$n,\$d,\$m,\$o,\$r,\$y) == one(\$s,\$e,\$n,\$d,\$m,\$o,\$r,\$y)
{
say "\$s\$e\$n\$d + \$m\$o\$r\$e == \$m\$o\$n\$e\$y";
}
}

test(»1..9«, »0..9«, »0..9«, »0..9«, »1..9«, »0..9«, »0..9«, »0..9«);

Using Damian's every() syntax, the last line becomes:

test(every(1..9), every(0..9), every(0..9), every(0..9),
every(1..9), every(0..9), every(0..9), every(0..9));

It's extremely brute force and inefficient, but it works.

I have the philosophical problem with your use of junctions in this
context due to the fact that you are completely ignoring the predicate
of the junction. The C< all(...) == one(...) > is an excellent use of
junctions, that makes use of the predicates when the junctions are
evaluated. If you want threading without the predicate, I give you the
hyperthreader (well, I'm trying to).

I'll also point out that what you wrote above is a lot more readable as:

for 1..9 -> \$a {
for 1..9 -> \$b {
next unless \$b == none(\$a);
for 0..9 -> \$c {
next unless \$c == none(\$a, \$b);
if \$a + \$b == \$a * 10 + \$c {
say "\$a + \$b = \$a\$c"
}
}
}
}

>
> I mean, wouldn't it really be nice if you could write stuff like this:
>
>   my @users is persistent("users.isam");
>   my @accounts is persistent("accounts.isam");
>
>   my \$r_user = any(@user);
>   my \$r_account = any(@account);
>
>   if ( \$r_account.user == \$r_user ) {
>       say("That junction is:", \$r_user);
>   }

I'm fairly certain that I'll be writing a Set class if no one beats me
to it.

use Sets;

my @users is persistent("users.isam");
my @accounts is persistent("accounts.isam");

my \$s_validusers = set(@accounts».user) - @users;

for values \$s_validusers -> \$user {
say "\$user is a valid user";
}

>
> \$r_user at that point represents only the users which have at least one
> object in @accounts for which there exists an \$r_account with a `user'
> property that is that \$r_user member[*].
>
> The closure would then either be run once, with \$r_user still a junction
> (if the interpreter/`persistent' class was capable of doing so), or once
> for every matching tuple of (\$r_account, \$r_user).  We're still
> talking in
> terms of Set Theory, right?

If you want Set Theory.. use Sets. Or use some form of Logical
Programming which builds the sets implicitly for you,  and does all the
filtering and  backtracking you're asking for.

Both topics have been explored recently. The results of that exploration
can be summarized as:
- Sets would make a nifty module to have around.
- Integrating Logical Programming into Perl needs a lot more thought and
effort than will likely happen before 6.0.0. Modules grafting LP into
Perl and/or Parrot are welcome.