sort-like syntax

B

Babacio

Hi,

I have a problem that has something to do with globing or something
like that -- I don't understand it deeply...

I need a function that shuffle a list by blocks. Blocks are defined
using a subroutine given as a parameter to the function.

Well, a piece of code is better than a long description:

#---------------------------------------------------------------------
use strict;
use List::Util qw(shuffle);

sub shuffle_blocks (&\@) {
my $code = shift();
my $r_tab = shift();
my @res;
my $i = 0;
my $N = scalar(@$r_tab);

my $a = $$r_tab[0];
while($i<$N) {
my @list = ();
my $b;
while( $i<$N and &{$code}($a,$b = $$r_tab[$i]) ) {
push @list, $b;
$i++;
}
push @res, (shuffle @list);
$a = $b;
}
return @res;
}

my %hash = ('a'=>1, 'B'=>2, 'c'=>1, 'd'=>1, 'E'=>2, 'f'=>1, 'G'=>2);
my @keys = keys %hash;

my @sorted = sort { $hash{$a} <=> $hash{$b} } @keys;
print "@sorted\n";
for(my $i =1; $i<10; $i++) {
my @shuf = shuffle_blocks { $hash{$_[0]} == $hash{$_[1]} } @sorted;
print "@shuf\n";
}

#--------------------------------------------------------

The output tastes like :

c a d f G E B
a f d c G B E
c a f d B G E
f c d a B G E
d a f c G E B
c f d a B G E
f a c d B E G
f a c d G E B
a d c f E G B
f c a d B E G

I think this example makes clear what I am doing.

Now here is my question: I want to modify shuffle_blocks so that I
can use it like this:

shuffle_blocks { $hash{$a} == $hash{$b} } @sorted;

It's not very important, but if there is an easy way to do that, I'd
like to know it. I tried various things, but they behave bad if $a, $b
are already defined before the call to the function...
 
M

Mumia W.

Babacio said:
Hi,

I have a problem that has something to do with globing or something
like that -- I don't understand it deeply...

I need a function that shuffle a list by blocks. Blocks are defined
using a subroutine given as a parameter to the function.

Well, a piece of code is better than a long description:

#---------------------------------------------------------------------
use strict;
use List::Util qw(shuffle);
[...]

Now here is my question: I want to modify shuffle_blocks so that I
can use it like this:

shuffle_blocks { $hash{$a} == $hash{$b} } @sorted;

It's not very important, but if there is an easy way to do that, I'd
like to know it. I tried various things, but they behave bad if $a, $b
are already defined before the call to the function...

I'm trying to learn Perl, and studying and changing your program was
very educational for me. Thank you. You should use localization for $a
and $b to make them available with the code block. This is what I did:

#!/usr/bin/perl
use strict;
use List::Util qw(shuffle);


sub shuffle_blocks_2 (&\@) {
my ($code, $r_tab) = @_;
local *table = $r_tab;
local ($a, $b);
my ($i, $N, @res) = (0, scalar(@::table));

$a = $::table[0];
while ($i<$N) {
my @list = ();

while ($i<$N) {
$b = $::table[$i];
last unless $code->();
push @list, $b;
$i++;
}

push @res, (shuffle @list);
$a = $b;
}

return @res;
}


my %hash = ('a'=>1, 'B'=>2, 'c'=>1, 'd'=>1, 'E'=>2, 'f'=>1, 'G'=>2);
my @keys = keys %hash;

my @sorted = sort { $hash{$a} <=> $hash{$b} } @keys;
print "@sorted\n";
for(my $i =1; $i<10; $i++) {
# my @shuf = shuffle_blocks { $hash{$_[0]} == $hash{$_[1]} } @sorted;
my @shuf = shuffle_blocks_2 { $hash{$a} == $hash{$b} } @sorted;
print "@shuf\n";
}
 
D

David H. Adler

sub shuffle_blocks (&\@) {

Not directly in answer to your question, but is there any particular
reason you're using a prototype there? Because prototypes are rarely
needed for perl subroutines, and their most common problem is people
using them when they don't have to...

dha
 
M

Mumia W.

David said:
Not directly in answer to your question, but is there any particular
reason you're using a prototype there? Because prototypes are rarely
needed for perl subroutines, and their most common problem is people
using them when they don't have to...

dha

He wants his subroutine to be callable in the same way as a Perl built-in.

my @blah = shuffle_blocks { ... } @blahblah;

Rather than,

my @blah = shuffle_blocks( {...}, \@blahblah);
 
P

Peter J. Holzer

Mumia said:
He wants his subroutine to be callable in the same way as a Perl
built-in.

my @blah = shuffle_blocks { ... } @blahblah;

Rather than,

my @blah = shuffle_blocks( {...}, \@blahblah);

my @blah = shuffle_blocks sub {...}, \@blahblah;

You don't need the parentheses, but you do need to declare the block as
an anonymous sub without the prototype.

hp
 
M

Matt Garrish

Mumia W. said:
Babacio said:
use strict;
use List::Util qw(shuffle);
[...]

Now here is my question: I want to modify shuffle_blocks so that I
can use it like this:

shuffle_blocks { $hash{$a} == $hash{$b} } @sorted;

It's not very important, but if there is an easy way to do that, I'd
like to know it. I tried various things, but they behave bad if $a, $b
are already defined before the call to the function...

I'm trying to learn Perl, and studying and changing your program was very
educational for me. Thank you. You should use localization for $a and $b
to make them available with the code block. This is what I did:

#!/usr/bin/perl
use strict;
use List::Util qw(shuffle);


sub shuffle_blocks_2 (&\@) {
my ($code, $r_tab) = @_;
local *table = $r_tab;

Why are you using a typeglob? If you want a copy of the array, just create
one.

my @table = @$r_tab;

Since nothing is being modified in the sub, you're just as safe using the
reference that gets passed in, as the OP did.
local ($a, $b);
my ($i, $N, @res) = (0, scalar(@::table));

That's a lot of ugly noise. Most people like that you can do things on fewer
lines in Perl, but when it makes a simple assignment of values look like
what you've done it 's best to split the declarations up:

my $i;
my $N = @table;
my @res;

my %hash = ('a'=>1, 'B'=>2, 'c'=>1, 'd'=>1, 'E'=>2, 'f'=>1, 'G'=>2);
my @keys = keys %hash;

my @sorted = sort { $hash{$a} <=> $hash{$b} } @keys;

There's no need to grab the keys into an array before sorting:

my @sorted = sort { $hash{$a} <=> $hash{$b} keys %hash;

But if you really want them sorted alphabetically and by case you should do:

my @sorted = sort { $hash{$a} <=> $hash{$b} || $a cmp $b } keys %hash;

Matt
 
B

Babacio

"Matt Garrish"

There's no need to grab the keys into an array before sorting:

my @sorted = sort { $hash{$a} <=> $hash{$b} keys %hash;
Yes...

But if you really want them sorted alphabetically and by case you
should do:

That's really not what I want to do... please read the original
post...
 
B

Babacio

David H. Adler said:
Not directly in answer to your question, but is there any particular
reason you're using a prototype there? Because prototypes are rarely
needed for perl subroutines, and their most common problem is people
using them when they don't have to...
Because I want to call it like that :

shuffle_blocks { (some code here) } @keys ;

and getting a reference to @keys instead of the whole array.
 
B

Babacio

Babacio.
"Mumia W."


Can it be so darnly simple ? I'll try that !

That's no good. Insert something like 'my $a = 10;' in the main
program, before the call to shuffle_blocks_2: it produces an infinite
loop...
 
M

Matt Garrish

Babacio said:
Babacio.


That's no good. Insert something like 'my $a = 10;' in the main
program, before the call to shuffle_blocks_2: it produces an infinite
loop...

Could you please explain why you're trying to use the special variables $a
and $b outside of sorting? If you want to write bad code you have to live
with the consequences.

Matt
 
D

David H. Adler

Because I want to call it like that :

shuffle_blocks { (some code here) } @keys ;

and getting a reference to @keys instead of the whole array.

That's a perfectly acceptable reason. So many people do it *without*
such a reason that I just thought I'd check.

Carry on. :)
 
M

Mumia W.

Babacio said:
Babacio.


That's no good. Insert something like 'my $a = 10;' in the main
program, before the call to shuffle_blocks_2: it produces an infinite
loop...

Then don't do that.

$a and $b are special variables used by sort(). That's why they don't
need to be declared 'our'. They work right when they are either full
globals or localized globals. Just don't make them lexical variables and
expect them to work.
 
B

Babacio

"Matt Garrish"
Could you please explain why you're trying to use the special
variables $a and $b outside of sorting? If you want to write bad
code you have to live with the consequences.

I was just wondering if it was possible to write a perl function
behaving like the built-in sort function.

I think it is easier for the user to write { $a <=> $b } than
{ $_[0] <=> $_[1] }.

But you may think it is a stupid idea. Don't take the time to tell it
if it is the case : whether it is stupid or not was not the question.
 
B

Babacio

"Mumia W."
Then don't do that.

$a and $b are special variables used by sort(). That's why they don't
need to be declared 'our'. They work right when they are either full
globals or localized globals. Just don't make them lexical variables and
expect them to work.

If I use $a and $b in my program, that does not pertub the built in
sort function. But that does perturb the shuffle_block function, which
is bad.
 
M

Matt Garrish

Babacio said:
"Matt Garrish"
Could you please explain why you're trying to use the special
variables $a and $b outside of sorting? If you want to write bad
code you have to live with the consequences.

I was just wondering if it was possible to write a perl function
behaving like the built-in sort function.

I think it is easier for the user to write { $a <=> $b } than
{ $_[0] <=> $_[1] }.

But you may think it is a stupid idea. Don't take the time to tell it
if it is the case : whether it is stupid or not was not the question.

That is not what you were asking, so please stick with your question. You
asked why you can't declare $a and $b and not have it affect the output. The
subroutine in $code is global in scope, so as soon as you declare $a or $b
at the package level those values get used by the anonymous sub in $code,
not the localized value in shuffle_blocks_2. This causes an infinite loop in
your code if you declare $a = 10, because there is no $hash{10}, so your
equality check is comparing "undef == 1" or "undef == 2", which are never
true.

Again, you shouldn't be doing stupid things like declaring special variables
for your own use, but you can get around the problem in this case by
explicitly passing in the values to compare:

my @shuf = shuffle_blocks_2 { my ($a, $b) = @_; $hash{$a} == $hash{$b} }
@sorted;

And then calling your function like this:

last unless $code->($a, $b);

This will ensure that the local $a and $b are used. But one last time **stop
trying to declare special variables**. Find some other more meaningful name
for your package variables. And if you don't like being told your coding
practices are bad, don't post bad code.

Matt
 
M

Matt Garrish

Matt Garrish said:
Babacio said:
"Matt Garrish"
Could you please explain why you're trying to use the special
variables $a and $b outside of sorting? If you want to write bad
code you have to live with the consequences.

I was just wondering if it was possible to write a perl function
behaving like the built-in sort function.

I think it is easier for the user to write { $a <=> $b } than
{ $_[0] <=> $_[1] }.

But you may think it is a stupid idea. Don't take the time to tell it
if it is the case : whether it is stupid or not was not the question.

That is not what you were asking, so please stick with your question. You
asked why you can't declare $a and $b and not have it affect the output.
The subroutine in $code is global in scope, so as soon as you declare $a
or $b at the package level those values get used by the anonymous sub in
$code, not the localized value in shuffle_blocks_2. This causes an
infinite loop in your code if you declare $a = 10, because there is no
$hash{10}, so your equality check is comparing "undef == 1" or "undef ==
2", which are never true.

Wrote that too hastily. I expect in a numeric comparison you would wind up
with "0 == 1" and "0 == 2", not undef.

Matt
 
M

Matt Garrish

Matt Garrish said:
Matt Garrish said:
Babacio said:
"Matt Garrish"

Could you please explain why you're trying to use the special
variables $a and $b outside of sorting? If you want to write bad
code you have to live with the consequences.

I was just wondering if it was possible to write a perl function
behaving like the built-in sort function.

I think it is easier for the user to write { $a <=> $b } than
{ $_[0] <=> $_[1] }.

But you may think it is a stupid idea. Don't take the time to tell it
if it is the case : whether it is stupid or not was not the question.

That is not what you were asking, so please stick with your question. You
asked why you can't declare $a and $b and not have it affect the output.
The subroutine in $code is global in scope, so as soon as you declare $a
or $b at the package level those values get used by the anonymous sub in
$code, not the localized value in shuffle_blocks_2. This causes an
infinite loop in your code if you declare $a = 10, because there is no
$hash{10}, so your equality check is comparing "undef == 1" or "undef ==
2", which are never true.

Wrote that too hastily. I expect in a numeric comparison you would wind up
with "0 == 1" and "0 == 2", not undef.

Nope, I was right the first time. I forgot whether an autovivified hash
value would be converted to 0, but it appears not. Had you turned on
warnings in your code you should have discovered most of what was going on
on your own. Please bear that in mind...

Matt
 
P

Peter J. Holzer

Babacio said:
"Mumia W."

If I use $a and $b in my program, that does not pertub the built in
sort function.

What version of perl are you using? perl 5.8.4 doesn't even compile the
program if you try to use sort inside a block with lexicals $a or $b:

my @x = qw(12 8 4 23 42 17 3);

my $a = 5;
my $b = 6;
my @y = sort {$a <=> $b} @x;
print "@y\n";

% perl foo2
Can't use "my $a" in sort comparison at foo2 line 5.

(even without warnings and strictures).
But that does perturb the shuffle_block function, which
is bad.

It is bad that it doesn't warn you that you are doing something stupid
while it does warn you if you commit the same stupidity with sort. But
that's because perl knows what sort is supposed to do, but it cannot
know what your own functions are supposed to do. It might warn you that
$a and $b are special and shouldn't be used as lexicals, but that might
be viewed as a bit extreme.

hp
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,066
Latest member
VytoKetoReviews

Latest Threads

Top