Re: Every other
Damian Conway wrote: > Mark Fowler wanted: > >> I have an array in Perl 5 [2]. I want *every* *other* element from it. > > Not especially fast, but very easy: > > use List::Maker; > > my @new = @old[ <0,2,..$#old> ]; That is splendidly magickal! -- David Cantrell | A machine for turning tea into grumpiness Support terrierism! Adopt a dog today!
Re: Every other
Nicholas Clark : >> my @new = @old[0,2...*]; > > That's a sequence of 0, 2, extrapolate? Yep. >> my @new = @old[ 0...*+2, 6 ]; > > Where's that's a sequence from 0 to whatever, with a stepping of 2? > What is the 6 for? Sorry, that's my brain spasming. The test code I was using had 6 elements. %-) General form would be: my @new = @old[ 0...*+2, +...@old ]; which means: "start at zero, increment by 2, stop at length of @old". Damian
Re: Every other
On Sat, Oct 31, 2009 at 08:07:08AM +1100, Damian Conway wrote: > Yes. Several in fact. The nicest is: > > my @new = @old[0,2...*]; That's a sequence of 0, 2, extrapolate? > That doesn't work yet in the current alpha release of Rakudo, but this > form does: > > my @new = @old[ 0...*+2, 6 ]; Where's that's a sequence from 0 to whatever, with a stepping of 2? What is the 6 for? Nicholas Clark
Re: Every other
Mark Fowler wanted: > I have an array in Perl 5 [2]. I want *every* *other* element from it. Not especially fast, but very easy: use List::Maker; my @new = @old[ <0,2,..$#old> ]; > There's very nice syntax for this in Perl 6, isn't there? Yes. Several in fact. The nicest is: my @new = @old[0,2...*]; That doesn't work yet in the current alpha release of Rakudo, but this form does: my @new = @old[ 0...*+2, 6 ]; Damian
Re: Every other
Torsten's solution was fastest: p...@teach:~/src/perl/scratch$ cat every-other.pl #!/usr/bin/env perl use strict; use warnings; use feature qw(say); use Benchmark qw(:all); my @data = 1..100; cmpthese( 50_000, { 'via array slice map' => \&via_array_slice_map, 'via array slice grep' => \&via_array_slice_grep, 'via hash' => \&via_hash, 'via part' => \&via_part, 'via grep bang' => \&via_grep_bang, 'via grep preinc' => \&via_grep_preinc, 'via grep xor' => \&via_grep_xor, 'via map' => \&via_map, }); # for testing only #local $,=','; #say via_array_slice_map(); #say via_array_slice_grep(); #say via_hash(); #say via_part(); #say via_grep_bang(); #say via_grep_preinc(); #say via_grep_xor(); #say via_map(); sub via_array_slice_map { return @data[map {$_*2} 0..$#data/2]; } sub via_array_slice_grep { return @data[ grep {!($_ & 1)} (0 .. $#data) ]; } sub via_hash { my %hash = @data; return (sort {$a <=> $b} keys %hash); } sub via_part { use List::MoreUtils qw(part); my $i = 0; my @part = part {$i++ % 2} @data; return @{$part[0]}; } sub via_grep_bang { my $i = 0; return grep { $i = !$i } @data; } sub via_grep_preinc { my $i = 0; return grep { ++$i % 2 } @data; } sub via_grep_xor { my $i = 0; return grep { $i ^= 1 } @data; } sub via_map { my $i = 1; return map {($i ^= 1) ? () : $_} @data; } p...@teach:~/src/perl/scratch$ perl every-other.pl Rate via part via hash via grep bang via array slice grep via grep preinc via map via array slice map via grep xor via part 28090/s -- -19% -29% -37%-41%-46%-52% -56% via hash 34483/s 23% -- -12% -22%-28%-34%-41% -46% via grep bang39370/s 40% 14%-- -11%-17%-24%-32% -39% via array slice grep 44248/s 58% 28% 12% -- -7%-15%-24% -31% via grep preinc 47619/s 70% 38% 21% 8% -- -9%-18% -26% via map 52083/s 85% 51% 32% 18% 9% ---10% -19% via array slice map 58140/s 107% 69% 48% 31% 22% 12% -- -9% via grep xor 64103/s 128% 86% 63% 45% 35% 23% 10% -- [eech, this table doesn't fit well in email. see http://www.doc.ic.ac.uk/~pgp/everyother.txt] so grep {$i ^= 1} @data; is fastest (on my machine at this phase of the moon etc), and somehow around 60% faster than grep {$i = !$i} @data; -- it surprises me that these aren't exactly the same.. I'd still stick with grep {++$i % 2} @data; for readability though... map beat two of the grep implementations despite protests that it copies rather than aliasing. Mapping was also better than grepping for forming index lists for array slices. part is unfairly disadvantaged since it is actually forming two lists, one of the evens and one of the odds, so it's not surprising that it is the slowest. In the end though, you only get a 2.25 factor gain of the best against the worst, so you might as well pick something readable. Phil 2009/10/30 Mark Fowler : > Hello, > > I have in the past coded things to only discover later that someone > else has already written what I have toiled away on, only better. So > this time, I'm asking the experts[1] first. > > I have an array in Perl 5 [2]. I want *every* *other* element from > it. There is, of course, more than one way to do it: > > my @new; > foreach (my $i = 0; $i < @old; $i++) { > push @new, $old[ $i ]; > } > > Or > > my $i; > my @new = grep { $i = !$i } @old; > > Or so on. None of these are particularly readable, or for that > matter, blindly efficient (they still use quite a few ops for such a > simple operation) > > What I would prefer is this: > > my @new = every_other @old; > > Which I guess could be generalised like so: > > i.e. > > everyother @array; > everyother @array, $offset; > everyother @array, $offset, $take_how_many; > everyother @array, $offset, $take_how_many, $skip_how_many; > > (with the default being everyother @array, 0, 1, 1) > > e.g. > > Ideally this would be a utility in List::MoreUtils or suchlike, but > it's not. Ideally it'd be implemented in C as well as in Perl so that > it doesn't burn ops for such a simple idea. > > Before I get going with the coding, does anyone know of anything else > that can do this? > > Mark. > > [1] experts on Buffy that is. Who might also happen to know some Perl. > [2] There's very nice syntax for this in Perl 6, isn't there?
Re: Every other
On Fri, 30 Oct 2009, at 11:13:24, Philip Potter wrote: >2009/10/30 Mark Fowler : > my $i; > my @new = grep { $i = !$i } @old; > >Thinking about it further, this could be more readable as: > >my @new = grep { ++$i % 2 } @old; > >[idea stolen from pod for List::MoreUtils::part] > >Phil > > In one step. my $bool = 0; # or 1 my @selection = grep { $bool ^= 1 } @data; Torsten
Re: Every other
Ruud H.G. van Tol wrote: Dirk Koopman wrote: Not certain why everyone avoids map and uses grep grep returns aliasses, map copies. Any advantage to the xor v the preincrement and remainder?
Re: Every other
Dirk Koopman wrote: Not certain why everyone avoids map and uses grep grep returns aliasses, map copies. -- Ruud
Re: Every other
Richard Huxton wrote: Philip Potter wrote: 2009/10/30 Mark Fowler : my $i; my @new = grep { $i = !$i } @old; Thinking about it further, this could be more readable as: my @new = grep { ++$i % 2 } @old; This one gets my vote for "the only one I'd want to come across without a comment above it". Not certain why everyone avoids map and uses grep, but here's my pennyworth: @a = (0..20); @b = map {($i ^= 1) ? () : $_} @a; print join(',', @b), "\n"; gives 1,3,5,7,9,11,13,15,17,19 Obviously if you reverse the () and the $_, you get: 0,2,4,6,8,10,12,14,16,18,20 Dirk
Re: Every other
On 30/10/2009 09:05, Mark Fowler wrote: Ideally this would be a utility in List::MoreUtils or suchlike, but it's not. C will do what you want. use List::MoreUtils 'part' my $i = 0; my @part = part { $i++ % 2 } 1 .. 8; # @part now contains reference to 2 lists, the odd and even elements It's not very clean, though, or particularly efficient. A
Re: Every other
Mark Fowler wrote: What I would prefer is this: my @new = every_other @old; ... Ideally this would be a utility in List::MoreUtils or suchlike, but it's not. Ideally it'd be implemented in C as well as in Perl so that it doesn't burn ops for such a simple idea. Before I get going with the coding, does anyone know of anything else that can do this? Just a note about Mark's question. He did point out that there were many ways to do it, but asked if anyone had already done it (whichever way) and put it behind a readable interface so he didn't have to do it himself. No-one's answered that yet (including me, because I don't know, but it doesn't seem to be covered explicitly by List::AllUtils). Ian
Re: Every other
Philip Potter wrote: > 2009/10/30 Mark Fowler : >> my $i; >> my @new = grep { $i = !$i } @old; > > Thinking about it further, this could be more readable as: > > my @new = grep { ++$i % 2 } @old; This one gets my vote for "the only one I'd want to come across without a comment above it". -- Richard Huxton Archonet Ltd
Re: Every other
On Oct 30, 2009, at 9:26 AM, Abigail wrote: On Fri, Oct 30, 2009 at 09:05:51AM +, Mark Fowler wrote: Hello, I have in the past coded things to only discover later that someone else has already written what I have toiled away on, only better. So this time, I'm asking the experts[1] first. I have an array in Perl 5 [2]. I want *every* *other* element from it. There is, of course, more than one way to do it: my @new; foreach (my $i = 0; $i < @old; $i++) { push @new, $old[ $i ]; } Assuming no duplicates or references, and an even sized list: my @odds = keys %...@old}}; my @evens = values %...@old}}; Oh, and it won't preserve order either. But at least it's not ugly. And it doesn't need additional variables. Abigail "The blue coat is mine" Why not something lengthier like @evens = @list[ grep {!($_ & 1)} (0 .. $#list) ]; which should preserve order and doesn't care about list size? Mike -- Mike Stok http://www.stok.ca/~mike/ The "`Stok' disclaimers" apply.
Re: Every other
On Fri, Oct 30, 2009 at 09:05:51AM +, Mark Fowler wrote: > Hello, > > I have in the past coded things to only discover later that someone > else has already written what I have toiled away on, only better. So > this time, I'm asking the experts[1] first. > > I have an array in Perl 5 [2]. I want *every* *other* element from > it. There is, of course, more than one way to do it: > > my @new; > foreach (my $i = 0; $i < @old; $i++) { > push @new, $old[ $i ]; > } Assuming no duplicates or references, and an even sized list: my @odds = keys %...@old}}; my @evens = values %...@old}}; Oh, and it won't preserve order either. But at least it's not ugly. And it doesn't need additional variables. Abigail "The blue coat is mine"
Re: Every other
2009/10/30 Mark Fowler : > my $i; > my @new = grep { $i = !$i } @old; Thinking about it further, this could be more readable as: my @new = grep { ++$i % 2 } @old; [idea stolen from pod for List::MoreUtils::part] Phil
Re: Every other
On Fri, Oct 30, 2009 at 11:32:43AM +0100, Philippe Bruhat (BooK) wrote: > On Fri, Oct 30, 2009 at 10:14:14AM +, Philip Potter wrote: > > > > Actually this is one thing that annoys me about Perl -- there doesn't > > seem to be a definitive standard to say whether these guarantees are > > cast-iron or whether they might change with new versions of perl. Or > > if there is, I haven't seen one. I miss the certainty of the C > > standard... > > > > Well, there's the Perl test suite. I'd say that for the op/ part, > I expect it to be very definitive and cast-iron. And if it's not, and that bothers you, submit a new test. -- Paul Johnson - p...@pjcj.net http://www.pjcj.net
Re: Every other
On Fri, Oct 30, 2009 at 11:35, Mark Fowler wrote: > Compared to: > > perl -MO=Terse -e 'my $i; @new = grep { $i != $i } @old;' Irrelevant, since that doesn't do what you want. (Hint: swap '=' and '!'.) Cheers, Philip -- Philip Newton
Re: Every other
On Fri, Oct 30, 2009 at 9:48 AM, Bob MacCallum wrote: > I clearly have more urgent things to do... > > @new = @old[map $_*2, 0..$#old/2]; Compact, but not overly readable and op-wise not ultra efficient: perl -MO=Terse -e ' @new = @old[map $_*2, 0..$#old/2];' LISTOP (0x100208ca0) leave [1] OP (0x100202620) enter COP (0x100208c40) nextstate BINOP (0x100208c00) aassign [13] UNOP (0x100209de0) null [142] OP (0x100208bd0) pushmark LISTOP (0x100209d00) aslice OP (0x100209d40) pushmark LOGOP (0x100209cc0) mapwhile [12] LISTOP (0x100209c20) mapstart OP (0x100209c60) pushmark UNOP (0x100209c90) null BINOP (0x1002091a0) multiply [6] UNOP (0x100209140) null [15] PADOP (0x100209110) gvsv GV (0x100804e28) *_ SVOP (0x100209170) const [15] IV (0x100813058) 2 UNOP (0x100209bf0) null UNOP (0x100206d30) flop UNOP (0x100206d00) flip [11] LOGOP (0x100206cc0) range [10] SVOP (0x1002e3240) const [16] IV (0x100804fd8) 0 BINOP (0x100206c80) divide [9] UNOP (0x100206c20) av2arylen UNOP (0x100206bf0) rv2av [8] PADOP (0x100206bc0) gv GV (0x100812fe0) *old SVOP (0x100206c50) const [14] IV (0x100813040) 2 UNOP (0x100220830) rv2av [4] PADOP (0x10020a6f0) gv GV (0x100812fe0) *old UNOP (0x100209d70) null [142] OP (0x100209db0) pushmark UNOP (0x100208eb0) rv2av [2] PADOP (0x100201c30) gv GV (0x100812f98) *new -e syntax OK Compared to: perl -MO=Terse -e 'my $i; @new = grep { $i != $i } @old;' LISTOP (0x100208f60) leave [1] OP (0x100202620) enter COP (0x100208fd0) nextstate OP (0x100201c30) padsv [1] COP (0x100209d50) nextstate BINOP (0x100209d10) aassign [7] UNOP (0x100209ca0) null [142] OP (0x100209ce0) pushmark LOGOP (0x100206d10) grepwhile [6] LISTOP (0x100206ca0) grepstart OP (0x100206ce0) pushmark UNOP (0x100206d50) null UNOP (0x100206c70) null LISTOP (0x100206b50) scope OP (0x100209150) null [177] BINOP (0x100209110) ne OP (0x100220830) padsv [1] OP (0x1002e3240) padsv [1] UNOP (0x100206c10) rv2av [5] PADOP (0x1002091b0) gv GV (0x100813058) *old UNOP (0x100209c60) null [142] OP (0x100206c40) pushmark UNOP (0x10020a6f0) rv2av [3] PADOP (0x100208eb0) gv GV (0x100812fc8) *new -e syntax OK Either way, it's a lot of ops to pull things out of an array.
Re: Every other
On Fri, Oct 30, 2009 at 10:14:14AM +, Philip Potter wrote: > > Actually this is one thing that annoys me about Perl -- there doesn't > seem to be a definitive standard to say whether these guarantees are > cast-iron or whether they might change with new versions of perl. Or > if there is, I haven't seen one. I miss the certainty of the C > standard... > Well, there's the Perl test suite. I'd say that for the op/ part, I expect it to be very definitive and cast-iron. -- Philippe Bruhat (BooK) Friendship is just brotherhood with a choice of brothers. (Moral from Groo #9 (Image))
Re: Every other
2009/10/30 Mark Fowler : > Hello, > > I have in the past coded things to only discover later that someone > else has already written what I have toiled away on, only better. So > this time, I'm asking the experts[1] first. > > I have an array in Perl 5 [2]. I want *every* *other* element from > it. There is, of course, more than one way to do it: > > my @new; > foreach (my $i = 0; $i < @old; $i++) { > push @new, $old[ $i ]; > } ew, C-style for loops :( Also, I think you meant $i+=2 > Or > > my $i; > my @new = grep { $i = !$i } @old; Is grep guaranteed to execute the code block on elements sequentially? Might it (if not now, possibly in future) dispatch different blocks to different processing elements, which would kinda screw this up? Actually this is one thing that annoys me about Perl -- there doesn't seem to be a definitive standard to say whether these guarantees are cast-iron or whether they might change with new versions of perl. Or if there is, I haven't seen one. I miss the certainty of the C standard... > What I would prefer is this: > > my @new = every_other @old; > > Which I guess could be generalised like so: > > i.e. > > everyother @array; > everyother @array, $offset; > everyother @array, $offset, $take_how_many; > everyother @array, $offset, $take_how_many, $skip_how_many; That syntax requires prototypes, which in general you should avoid. > Ideally this would be a utility in List::MoreUtils or suchlike, but > it's not. Ideally it'd be implemented in C as well as in Perl so that > it doesn't burn ops for such a simple idea. > > Before I get going with the coding, does anyone know of anything else > that can do this? If the array is sorted and has no duplicate values, and runtime costs haven't been demonstrated to be an issue, then what about: my %hash = @array; my @array1 = sort keys %hash; my @array2 = sort values %hash; ;) Standard are-you-sure questions: Is there a reason you are so concerned with it being as fast as possible? Are you solving a problem which needs to be solved? Do you have a project in which the cost of taking every other element has been profiled and measured and shown to be significant? Why do you need to take every other element of an array in the first place? Could you have rewritten your code or redesigned your data structures so that the data you want is already in the form you want it? If you need the array just to loop through it, why not do something like: while (($x) = splice @a, 0, 2) { # all parens are necessary # loop here } > [2] There's very nice syntax for this in Perl 6, isn't there? I'm not > using that language yet. There's a for loop syntax which allows you to iterate over every other element easily: http://perlcabal.org/syn/Differences.html#foreach_becomes_for I'm not sure there's a nice syntax to just create the array. Phil
Re: Every other
I clearly have more urgent things to do... @new = @old[map $_*2, 0..$#old/2]; On Fri, Oct 30, 2009 at 9:21 AM, Bob MacCallum wrote: > If your array is numeric: > > use PDL; > my @old = (1..10); > my @new = pdl(@old)->slice("0:-1:2")->list; > > although this is unlikely to be the most efficient way either (due to > the conversion between arrays and piddles). > > > > On Fri, Oct 30, 2009 at 9:05 AM, Mark Fowler wrote: >> Hello, >> >> I have in the past coded things to only discover later that someone >> else has already written what I have toiled away on, only better. So >> this time, I'm asking the experts[1] first. >> >> I have an array in Perl 5 [2]. I want *every* *other* element from >> it. There is, of course, more than one way to do it: >> >> my @new; >> foreach (my $i = 0; $i < @old; $i++) { >> push @new, $old[ $i ]; >> } >> >> Or >> >> my $i; >> my @new = grep { $i = !$i } @old; >> >> Or so on. None of these are particularly readable, or for that >> matter, blindly efficient (they still use quite a few ops for such a >> simple operation) >> >> What I would prefer is this: >> >> my @new = every_other @old; >> >> Which I guess could be generalised like so: >> >> i.e. >> >> everyother @array; >> everyother @array, $offset; >> everyother @array, $offset, $take_how_many; >> everyother @array, $offset, $take_how_many, $skip_how_many; >> >> (with the default being everyother @array, 0, 1, 1) >> >> e.g. >> >> Ideally this would be a utility in List::MoreUtils or suchlike, but >> it's not. Ideally it'd be implemented in C as well as in Perl so that >> it doesn't burn ops for such a simple idea. >> >> Before I get going with the coding, does anyone know of anything else >> that can do this? >> >> Mark. >> >> [1] experts on Buffy that is. Who might also happen to know some Perl. >> [2] There's very nice syntax for this in Perl 6, isn't there? I'm not >> using that language yet. >> > > > > -- > http://evolectronica.com - survival of the funkiest > -- http://evolectronica.com - survival of the funkiest
Re: Every other
If your array is numeric: use PDL; my @old = (1..10); my @new = pdl(@old)->slice("0:-1:2")->list; although this is unlikely to be the most efficient way either (due to the conversion between arrays and piddles). On Fri, Oct 30, 2009 at 9:05 AM, Mark Fowler wrote: > Hello, > > I have in the past coded things to only discover later that someone > else has already written what I have toiled away on, only better. So > this time, I'm asking the experts[1] first. > > I have an array in Perl 5 [2]. I want *every* *other* element from > it. There is, of course, more than one way to do it: > > my @new; > foreach (my $i = 0; $i < @old; $i++) { > push @new, $old[ $i ]; > } > > Or > > my $i; > my @new = grep { $i = !$i } @old; > > Or so on. None of these are particularly readable, or for that > matter, blindly efficient (they still use quite a few ops for such a > simple operation) > > What I would prefer is this: > > my @new = every_other @old; > > Which I guess could be generalised like so: > > i.e. > > everyother @array; > everyother @array, $offset; > everyother @array, $offset, $take_how_many; > everyother @array, $offset, $take_how_many, $skip_how_many; > > (with the default being everyother @array, 0, 1, 1) > > e.g. > > Ideally this would be a utility in List::MoreUtils or suchlike, but > it's not. Ideally it'd be implemented in C as well as in Perl so that > it doesn't burn ops for such a simple idea. > > Before I get going with the coding, does anyone know of anything else > that can do this? > > Mark. > > [1] experts on Buffy that is. Who might also happen to know some Perl. > [2] There's very nice syntax for this in Perl 6, isn't there? I'm not > using that language yet. > -- http://evolectronica.com - survival of the funkiest