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

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

Thread Previous | Thread Next
From:
Rod Adams
Date:
March 11, 2005 21:56
Subject:
Re: SEND + MORE = MONEY (works now in pugs with junctions!)
Message ID:
42328483.9000800@rodadams.net
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.

-- Rod Adams





Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About