see
Let's say for concreteness that we would like to solve this cryptarithm puzzle:This means that we want to map the letters S, E, N, D, M, O, R, Y to distinct digits 0 through 9 to produce a five-digit and two four-digit numerals which, when added in the indicated way, produce the indicated sum.S E N D + M O R E ----------- M O N E Y
the proposed Perl code was actually the semantic translation of some Python code and was indeed clumsy (NB all those curlies at the end)
sub bd { my ($ls, $f) = @_; [ map @{$f->($_)}, @$ls ] # Yow } sub guard { $_[0] ? [undef] : [] } sub remove { my ($b, $a) = @_; my %h = map { $_ => 1 } @$a; delete $h{$_} for @$b; return [ keys %h ]; } my $digits = [0..9]; my $solutions = bd remove([0], $digits) => sub { my ($s) = @_; bd remove([$s], $digits) => sub { my ($e) = @_; bd remove([$s,$e], $digits) => sub { my ($n) = @_; bd remove([$s,$e,$n], $digits) => sub { my ($d) = @_; my $send = "$s$e$n$d"; bd remove([0,$s,$e,$n,$d], $digits) => sub { my ($m) = @_; bd remove([$s,$e,$n,$d,$m], $digits) => sub { my ($o) = @_; bd remove([$s,$e,$n,$d,$m,$o], $digits) => sub { my ($r) = @_; my $more = "$m$o$r$e"; bd remove([$s,$e,$n,$d,$m,$o,$r], $digits) => sub { my ($y) = @_; my $money = "$m$o$n$e$y"; bd guard($send + $more == $money) => sub { [[$send, $more, $money]] +}}}}}}}}}; for my $s (@$solutions) { print "@$s\n"; }
Now here my solution reusing some work I've done in the past with List Comprehensions
use strict; use warnings; use Data::Dump qw/pp dd/; =pod https://blog.plover.com/prog/haskell/monad-search.html https://blog.plover.com/prog/monad-search-2.html =cut # --- List comprehension sub from (&$;$) { my ($c_block, undef, $c_tail) = @_; my $var = \$_[1]; sub { for ( &$c_block ) { $$var = $_; $c_tail->() } } } sub when (&$){ # guard my ($c_block, $c_tail) = @_; sub { $c_tail->() if &$c_block } } # --- rem() Helper function to return digits 0..9 except @_ my %digits; @digits{0..9}=(); sub rem { # set difference my %h = %digits; delete @h{@_}; keys %h; } my ($send,$s,$e,$n,$d); my ($more,$m,$o,$r); my ($money,$y); my $do = # send from { rem 0 } $s => from { rem $s } $e => from { rem $s, $e } $n => from { rem $s,$e,$n } $d => # more from { rem 0,$s,$e,$n,$d } $m => from { rem $s,$e,$n,$d,$m } $o => from { rem $s,$e,$n,$d,$m,$o } $r => # money from { rem $s,$e,$n,$d,$m,$o,$r } $y => # guard when { "$s$e$n$d" + "$m$o$r$e" == "$m$o$n$e$y" } # output sub { pp [ "$s$e$n$d" , "$m$o$r$e" , "$m$o$n$e$y" ] } ; &$do;
Compilation started at Wed Dec 2 19:11:14 C:/Perl_524/bin\perl.exe -w d:/tmp/pm/send_more_money_monad.pl [9567, 1085, 10652] Compilation finished at Wed Dec 2 19:11:18
NB: It could be done better and faster, I've just coded this POC from scratch within an hour.
Please note that making it "lazy" is not really a problem, left for the interested reader.
So Monads in Perl are not that complicated ... or what am I missing? ;-)
Cheers Rolf
(addicted to the Perl Programming Language :)
Wikisyntax for the Monastery
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: RFC: Monads in Perl (Send + More = Money)
by LanX (Saint) on Dec 13, 2020 at 17:28 UTC |