Re: Every other

2009-10-30 Thread David Cantrell
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

2009-10-30 Thread Damian Conway
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

2009-10-30 Thread Nicholas Clark
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

2009-10-30 Thread Damian Conway
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

2009-10-30 Thread Philip Potter
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

2009-10-30 Thread Torsten Knorr
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

2009-10-30 Thread Dirk Koopman

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

2009-10-30 Thread Ruud H.G. van Tol

Dirk Koopman wrote:


Not certain why everyone avoids map and uses grep


grep returns aliasses, map copies.

--
Ruud


Re: Every other

2009-10-30 Thread Dirk Koopman

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

2009-10-30 Thread Andy Wardley

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

2009-10-30 Thread Ian Malpass

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

2009-10-30 Thread Richard Huxton
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

2009-10-30 Thread Mike Stok


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

2009-10-30 Thread Abigail
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 Thread Philip Potter
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

2009-10-30 Thread Paul Johnson
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

2009-10-30 Thread Philip Newton
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

2009-10-30 Thread Mark Fowler
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

2009-10-30 Thread Philippe Bruhat (BooK)
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 Thread Philip Potter
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

2009-10-30 Thread Bob MacCallum
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

2009-10-30 Thread Bob MacCallum
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