Re: 99 Problems in Perl6 (Lisp, Prolog, Haskell)
by Ovid (Cardinal) on Dec 15, 2006 at 21:57 UTC
|
How embarrassing. My first version of problem 9 (packing lists into sublists) had a bug. Here's a cleaner version which actually works. I know the bug in problem 9. Can you spot it?
sub pack (@array) returns Array {
my @unpacked = @array;
my (@list, @packed);
while @unpacked {
@list.push(@unpacked.shift) while !@list || @list[0] eq @unpac
+ked[0];
@packed.push([@list]);
@list = ();
}
return @packed;
}
pack(<a a a a b c c a a d e e e e>).perl.say;
And for the Lisp weenies who claim Lisp is so much better, here's one way to do it in Lisp (can you make it shorter?):
(defun pack (lista)
(if (eql lista nil)
nil
(cons (pega lista) (pack (tira lista)))
)
)
(defun pega (lista)
(cond ((eql lista nil) nil)
((eql (cdr lista) nil) lista)
((equal (car lista) (cadr lista))
(cons (car lista) (pega (cdr lista))))
(t (list (car lista)))
)
)
(defun tira (lista)
(cond ((eql lista nil) nil)
((eql (cdr lista) nil) nil)
((equal (car lista) (cadr lista))
(tira (cdr lista)))
(t (cdr lista))
)
)
But the Prolog folks still have a neat solution:
pack([],[]).
pack([X|Xs],[Z|Zs]) :- transfer(X,Xs,Ys,Z), pack(Ys,Zs).
transfer(X,[],[],[X]).
transfer(X,[Y|Ys],[Y|Ys],[X]) :- X \= Y.
transfer(X,[X|Xs],Ys,[X|Zs]) :- transfer(X,Xs,Ys,Zs).
And Haskell still makes us all look like chumps:
group (x:xs) = let (first,rest) = span (==x) xs
in (x:first) : group rest
group [] = []
| [reply] [d/l] [select] |
|
Here's a prettier way to write the Perl 6 solution:
sub group (*@array is copy) {
gather {
while @array {
take [
gather {
my $h = shift @array;
take $h;
while @array and $h eq @array[0] {
take shift @array;
}
}
];
}
}
}
It would be slightly prettier if take could harvest a value en passant.
| [reply] [d/l] |
|
Sweet!
Why do you flatten the array? I played with that in Pugs and the asterisk seems superfluous. It seems like one is copy the elements and the other is copying the array. I thought that maybe your version would avoid aliasing problems, but even in checking that, I'm not seeing it happening.
sub group (@array is copy) { # didn't flatten
gather {
while @array {
take [
gather {
my $h = shift @array;
take $h;
while @array and $h eq @array[0] {
take shift @array;
}
}
];
}
}
}
| [reply] [d/l] |
|
|
atoku says:
Lisp deserves better code :) Below is just an example of a possible better way ;) just 6 lines.
(defun pick-new (lst el)
(if (eql (car (car lst)) el)
(cons (cons el (car lst)) (cdr lst))
(cons (list el) lst)))
(defun pack (lst)
(reverse (reduce #'pick-new lst :initial-value nil)))
Winston Smith says:
Here is a variation on Atoku’s elegant lisp solution. Note the unification of cases made possible by the side-effect of pop.
(defun pick-new (lst el)
(cons (cons el (when (equal el (caar lst)) (pop lst))) lst))
(defun pack (lst)
(reverse (reduce #'pick-new lst :initial-value nil)))
Here are three solutions in lisp that don’t require auxiliary functions.
;;With loop:
(defun pack(lst &optional groups)
(loop for el in lst
for first-group = (when (equal el (caar groups)) (pop groups))
do (push (cons el first-group) groups))
(reverse groups))
;;With recursion:
(defun pack(lst &optional groups)
(if (not lst) (reverse groups)
(let* ((el (pop lst))
(first-group (when (equal el (caar groups)) (pop groups))
+))
(pack lst (cons (cons el first-group) groups)))))
;;With no mercy:
(defun pack(lst &optional g)
(if (not lst) (reverse g)
(pack (cdr lst) (cons (cons (car lst) (when (equal (car lst) (ca
+ar g)) (pop g))) g)))))
| [reply] [d/l] [select] |
Re: 99 Problems in Perl6
by BrowserUk (Patriarch) on Dec 17, 2006 at 12:22 UTC
|
Seems that most of the Perl 6 solutions (never mind the hidious lisp concoctions:) are much clumsier than a fairly obvious Perl 5 solution?
Updated: To correctly handle a null list per TimToady's correction below.
sub p09{
@_?reduce{$a->[-1][0]eq$b?push@{$a->[-1]},$b:push@{$a},[$b];$a;}[[shif
+t]],@_:()
}
which could almost certainly be golfed further, but for the queasy, expands out to a relatively clean:
use List::Util qw[ reduce ];
sub p09 {
@_ ? reduce{
$a->[-1][0] eq $b
? push @{ $a->[ -1 ] }, $b
: push @{ $a }, [ $b ];
$a;
} [[shift]], @_
:()
}
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] [select] |
|
Minor quibble: that would appear to generate a different result when you feed it the null list.
| [reply] |
|
Good catch++. Updated.
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] |
Re: 99 Problems in Perl6
by gaal (Parson) on Dec 15, 2006 at 22:15 UTC
|
In #8, you have a superfluous assignment of the closure. Instead of
my $compress = do {
my $previous;
$compress = sub ($x) { ... }
};
You could just arrange for the sub definition to be the last evaluated thing inside the do. (This is really no different from Perl 5, except for the formal parameter.)
my $compress = do {
my $previous;
sub ($x) { ... }
};
# or perhaps,
my $compress;
{
my $previous;
$compress = sub ($x) { ... }
}
| [reply] [d/l] [select] |
|
On second thought, the only reason you have a closure here at all is to preserve state between calls. Use a state variable then, and the more normal function definition syntax.
sub compress ($x) {
state $previous;
if $x ne $previous {
return $x;
}
else {
return;
}
}
Update: I think the if is way too long, but can't remember if there's a context-safe way to return nothing in Perl 6 (that isn't "fail"), to golf out the "return" and say something like return $x ne $previous ?? $x !! $NOTHING_AT_ALL;. In the current application () is probably safe. | [reply] [d/l] [select] |
|
my $compress = sub ($x) {
state $previous;
$x ne $previous ?? $previous = $x !! return;
}
And in Haskell:
compress :: Eq a => [a] -> [a]
compress = map head . group
Update: as concise, but less "golfy":
my $compress = sub ($x) {
state $previous;
return $x ne $previous ?? $previous = $x !! ();
}
| [reply] [d/l] [select] |
|
|
|
|
|
my $compress = do {
my $previous;
sub ($x) {
$x ne $previous ?? $previous = $x !! return;
};
}
That works just fine, but it might give someone pause :) Of course, I wouldn't write it like that for production.
| [reply] [d/l] |
|
... ?? $previous = $x !! ...;
would be a syntax error. You need parens (I think Pugs lets you get away with this for now but that will get fixed eventually).
| [reply] [d/l] |
|
|
my @in = <a a a a b c c a a d e e e e>;
my @out = gather {
for @in -> $i {
state $last;
take $i if $i ne $last;
$last = $i;
}};
say @out;
| [reply] [d/l] |
|
TIMTOWTDI of course, but syntax updates to gather since the OP allow you to write it as:
my @in = <a a a a b c c a a d e e e e>;
say gather for @in -> $i {
state $last;
$last = take $i if $i ne $last;
};
| [reply] [d/l] [select] |
|
Re: 99 Problems in Perl6
by jdporter (Paladin) on Dec 15, 2006 at 22:31 UTC
|
@flattened.perl.say;
What is this .perl method? Needless to .say, it's not easy to search for.
We're building the house of the future together.
| [reply] [d/l] |
|
It's kind of like a poor man's Data::Dumper. For example, if you want to see all of your environment variables, you can use this:
%*ENV.perl.say;
That will represent %*ENV (remember that the '*' twigil means it's global) in a way that's legal Perl6 code. I use it because it makes complex data structures much easier to read. I don't know if it's possible to customize the output, though. Right now it's all one one line.
You can still do this:
%*ENV.say;
You won't find the output as legible, though.
And while we're on the topic of environment variables, use the '+' twigil to read them. What to see the 'HOME' environment variable?
$+HOME.say
Update: the one thing I really want is for reflection to be added to Pugs so I can query an object for its methods. That will be a huge productivity boost so we can see exactly what we can play with.
| [reply] [d/l] [select] |
|
What Ovid said. There's also a .yaml method that does the YAML::Syck::Dump equivalent. The round trip is also analogous:
eval $obj.perl;
eval $obj.yaml :lang<yaml>; # though this syntax is unspecced and
+may change.
| [reply] [d/l] [select] |
|
From S02:
To get a Perlish representation of any object, use the .perl method. Like the Data::Dumper module in Perl 5, the .perl method will put quotes around strings, square brackets around list values, curlies around hash values, constructors around objects, etc., so that Perl can evaluate the result back to the same object.
| [reply] |
As Jay-Z says
by perrin (Chancellor) on Dec 16, 2006 at 00:06 UTC
|
"If you're having perl problems I feel bad for you son, I got 99 problems but Perl 6 ain't one"
Sorry, I couldn't resist.
| [reply] |
|
| [reply] |
Re: 99 Problems in Perl6
by Juerd (Abbot) on Dec 15, 2006 at 20:56 UTC
|
| [reply] |
|
Well, to be honest, I should write tests instead, but this was a quick start to just show the answers. By doing it this way, we don't have to worry about explaining "this is how you test code", but I suppose it would be better that way.
| [reply] |
Re: 99 Problems in Perl6
by Util (Priest) on Dec 17, 2006 at 01:09 UTC
|
Here are my solutions to Problem 9. The first is concise and looks fairly efficient.
The second is less straightforward; I wanted to solve it with one big map, in
the hopes of allowing it to work lazily. I don't know how to tell if I
succeeded, though.
Code tested in Pugs:
sub p09_group_Util_1 (*@list) {
my @ret;
for @list -> $val {
push @ret, [] if !@ret or $val !~~ @ret[-1][-1];
push @ret[-1], $val;
}
return @ret;
}
sub p09_group_Util_2 (*@list) {
my @tmp;
return map -> $index, $val {
my @ret;
if !@tmp or $val !~~ @tmp[-1] {
@ret = [ @tmp ] if @tmp;
undefine @tmp;
}
push @tmp, $val;
push @ret, [ @tmp ] if @tmp and $index == @list.end;
@ret; # May contain 0, 1, or 2 arrayrefs.
}, @list.kv;
}
my @data = <a a a a b c c a a d e e e e>;
say '9.1 ', p09_group_Util_1(@data).perl;
say '9.2 ', p09_group_Util_2(@data).perl;
# When dumped via .perl, Util_1 is wrapped in [], and Util_2 in ().
# When dumped via .yaml, Util_1 and Util_2 are identical. Bug?
Output:
9.1 [["a", "a", "a", "a"], ["b",], ["c", "c"], ["a", "a"], ["d",], ["e
+", "e", "e", "e"]]
9.2 (["a", "a", "a", "a"], ["b",], ["c", "c"], ["a", "a"], ["d",], ["e
+", "e", "e", "e"])
| [reply] [d/l] [select] |