Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

seeking golfing advice

2 views
Skip to first unread message

damien krotkine

unread,
May 16, 2012, 7:15:53 AM5/16/12
to f...@perl.org
Hi,

I'm using this code to get a list of only the odd elements of an
array. The resulting list must have the same order as the array.

map { state $f; ($_) x (++$f%2) } @array;

I'm looking for advice to make it shorter or nicer. Everything in perl
5.12 is allowed, but must pass use strict. I've failed at using the
'..' operator to act as a flip/flop operator...

thanks,
dams

damien krotkine

unread,
May 16, 2012, 7:19:01 AM5/16/12
to f...@perl.org
btw here is an example :

the code, applied on (1, 2, 3, 4) would return (1, 3). Thanks

Daniel Cutter

unread,
May 16, 2012, 7:29:21 AM5/16/12
to f...@perl.org
Isn't this what grep is for?

1 #!perl
2 use strict;
3 my @array=(1,2,3,4);
4 my $f;
5 my @result = grep {++$f%2} @array;
6 print "@result\n";

--
*Daniel Cutter*

s/\b[^a]/\u$&/g,s;$;,;,print for join$,,map{('acehjklnoprstu'
=~m(.)g,$")[/\d/?$_:hex]}q(4dbce078c32ae92a6e30152aff)=~m(.)g

Peter Makholm

unread,
May 16, 2012, 7:30:43 AM5/16/12
to f...@perl.org
damien krotkine <dkro...@gmail.com> writes:

> I'm using this code to get a list of only the odd elements of an
> array. The resulting list must have the same order as the array.
>
> map { state $f; ($_) x (++$f%2) } @array;

If you want only to get some elements of a list is is much more obvious
to use grep instead of map:

grep { ++$f%2 } @array

Then use the magical flip-flop variable:

grep { $|-- } @array

And remove unneeded syntax

grep$|--,@array

//Makholm

John Douglas Porter

unread,
May 16, 2012, 7:35:38 AM5/16/12
to f...@perl.org
It's not clear whether you want "every other element, beginning with the first" or "every numeric element with the property 'odd'". Your example doesn't clear that up at all. :-)

If it's the latter you want:

map { $_ & 1 ? $_ : () } @l;


--- On Wed, 5/16/12, damien krotkine <dkro...@gmail.com> wrote:

Peter Makholm

unread,
May 16, 2012, 7:36:18 AM5/16/12
to f...@perl.org
Peter Makholm <pe...@makholm.net> writes:

> damien krotkine <dkro...@gmail.com> writes:
>
>> I'm using this code to get a list of only the odd elements of an
>> array. The resulting list must have the same order as the array.
>>
>> map { state $f; ($_) x (++$f%2) } @array;

Wow, you asked for golfing advice in the subject and a nice solution in
the actual text. Those requirements are quite often very incompatible.

> If you want only to get some elements of a list is is much more obvious
> to use grep instead of map:
>
> grep { ++$f%2 } @array

This is probably as nice as it gets. For strictness I would just declare
$f as a lexical variable just outside the grep. But asking for golfing
advice is not compliant with strictness or niceness.

> And remove unneeded syntax
>
> grep$|--,@array

I think this would be the golfing solution, but as with everything alse
related to perl golfing don't use it in production code.

//Makholm

John Douglas Porter

unread,
May 16, 2012, 7:40:40 AM5/16/12
to f...@perl.org
And of course, use grep, as others have said.

@list[ grep !$_%2, 0..$#list ];

that gets you every other element, beginning with the first.


--- On Wed, 5/16/12, John Douglas Porter <johnd...@yahoo.com> wrote:

damien krotkine

unread,
May 16, 2012, 7:46:32 AM5/16/12
to Peter Makholm, f...@perl.org
Hi all,

thanks for the quick answers :)

first of all, despite my attempt to clear things up with an example, I
failed :) What I wante is "every other element, beginning with the
first", so :

( 'foo', 3, 42, 'bar) should become ( 'foo', 42).

Indeed grep is much better. As the code is used in a more complex
structured I got lost and confused and ended up using map, blah.

$|-- was what I was looking for :)

About golfing and strictness I am well aware that the two are
incompatible. What I originally meant is to get something with a sane
balance between the two.

Finally, one question : why is $|-- not recommended in production, if
the following conditions are met : I'm not running in threads, not in
an event-loop that might be affected by $| being set, and I'll take
care of resetting $| appropriately at the end.

Even in this case, wouldn't you recommend using $| ?


Thanks again

Peter Makholm

unread,
May 16, 2012, 8:02:36 AM5/16/12
to f...@perl.org
damien krotkine <dkro...@gmail.com> writes:

> Indeed grep is much better. As the code is used in a more complex
> structured I got lost and confused and ended up using map, blah.
>
> $|-- was what I was looking for :)

No, reading my posts again it I see that it clearly doesn't do what you
ask for. You are actually looking for --$|.

> Finally, one question : why is $|-- not recommended in production, if
> the following conditions are met : I'm not running in threads, not in
> an event-loop that might be affected by $| being set, and I'll take
> care of resetting $| appropriately at the end.

It depends on an obscure, undocumented feature of a magic variable, which
unless used correct may have an unintended action at a distance. This
makes the code hard to maintain for the next developer (which might be
youself 14 days down the road).

What do you think you are optimizing for?

I would guess that the $| is also a bit slower, as it needs to at least
test if the filehandle needs to be flushed even though you know that no
one had the chance to write to it.

So basically you are trading 8 characters for readability and probably
speed. Why do you want that?

//Makholm


damien krotkine

unread,
May 16, 2012, 8:25:26 AM5/16/12
to Peter Makholm, f...@perl.org
On 16 May 2012 14:02, Peter Makholm <pe...@makholm.net> wrote:
> damien krotkine <dkro...@gmail.com> writes:
>
>> Indeed grep is much better. As the code is used in a more complex
>> structured I got lost and confused and ended up using map, blah.
>>
>> $|-- was what I was looking for :)
>
> No, reading my posts again it I see that it clearly doesn't do what you
> ask for. You are actually looking for --$|.

Oops yes, --$| indeed, sorry.

[ ... ]

> So basically you are trading 8 characters for readability and probably
> speed. Why do you want that?

For fun, purely. I'll stick with $f % 2 for now, it seems a right
balance compared to the code lying around.

dams

Mike Erickson

unread,
May 16, 2012, 9:41:31 AM5/16/12
to damien krotkine, Peter Makholm, f...@perl.org
On 05/16/2012 07:46 AM, damien krotkine wrote:
> Hi all,
>
> thanks for the quick answers :)
>
> first of all, despite my attempt to clear things up with an example, I
> failed :) What I wante is "every other element, beginning with the
> first", so :
>
> ( 'foo', 3, 42, 'bar) should become ( 'foo', 42).

If you don't care about order, but just want those elements, you can
also do:

keys%{{@a}}

{@a} being a hash ref constructed from @a
%{ } dereferencing that hash ref
keys pulling out the keys, ie. the odd elements of the orig. list

hth,

Mike

Moritz, Georg

unread,
May 16, 2012, 7:51:18 AM5/16/12
to f...@perl.org, damien krotkine
> btw here is an example :
>
> the code, applied on (1, 2, 3, 4) would return (1, 3). Thanks

so you want to check every element for oddity of its value, not its index, right?

@list = grep{$_%2}@array;

cheers,
0--gg-

> On 16 May 2012 13:15, damien krotkine <dkro...@gmail.com> wrote:
> > Hi,
> >
> > I'm using this code to get a list of only the odd elements of an
> > array. The resulting list must have the same order as the array.
> >
> > map { state $f; ($_) x (++$f%2) }  @array;
> >

Ronald J Kimball

unread,
May 16, 2012, 11:22:27 AM5/16/12
to John Douglas Porter, f...@perl.org
On Wed, May 16, 2012 at 04:40:40AM -0700, John Douglas Porter wrote:
> And of course, use grep, as others have said.
>
> @list[ grep !$_%2, 0..$#list ];
>
> that gets you every other element, beginning with the first.

! has higher precedence than %, so this actually gets you just the
first element.


You need to add parentheses to get the correct result:

@list[grep!($_%2),0..$#list];


There are a couple ways to shave a character from that:

@list[grep$_%2-1,0..$#list];
@list[grep$_+1&1,0..$#list];

Ronald

Aristotle Pagaltzis

unread,
May 17, 2012, 6:14:59 AM5/17/12
to f...@perl.org
* Mike Erickson <m...@quidquam.com> [2012-05-16 15:45]:
> If you don't care about order, but just want those elements, you can
> also do:
>
> keys%{{@a}}

There is more than order that gets lost. If you use `keys` you also get
everything back stringified – undefs are lost and references break. If
you use `values` these problems go away… except that to get the odd-
index elements from it you have to `reverse` the array, at which point
a not-especially-golfed grep is shorter.

--
*AUTOLOAD=*_;sub _{s/$/$"/;s/(.*):://;wantarray//substr$_,-1,1,",$/";print;$1}
&Just->another->Perl->hack;
#Aristotle Pagaltzis // <http://plasmasturm.org/>

Aristotle Pagaltzis

unread,
May 17, 2012, 6:28:20 AM5/17/12
to f...@perl.org
* Pau Amma <pau...@gundo.com> [2012-05-16 14:55]:
> If, as it sounds, you want to balance golfiness and strictness, you
> could also say:
>
> @array[grep $_%2, keys @array]
>
> (or @array[grep $_%2^1, keys @array] if you set $[ to 1 - but you
> didn't do that, right? :-) )

Btw, `keys@foo` and `0..$#foo` are equally long… but the latter works
with old perls (advantage production) *and* looks more line-noisy
(advantage golf).

And if you have an array to work with in the first place, you can also

@array[map 1+$_*2, 0..$#array/2]

which is 3 characters longer than the grep version in exchange for doing
half as much work. (For even elements, the map and grep solutions would
yield exactly equally long code, with half-as-much-work still applying.)

Steve Fink

unread,
May 17, 2012, 3:35:41 PM5/17/12
to f...@perl.org
On Thu, May 17, 2012 at 3:14 AM, Aristotle Pagaltzis <paga...@gmx.de> wrote:
> * Mike Erickson <m...@quidquam.com> [2012-05-16 15:45]:
>> If you don't care about order, but just want those elements, you can
>> also do:
>>
>> keys%{{@a}}
>
> There is more than order that gets lost. If you use `keys` you also get
> everything back stringified – undefs are lost and references break. If
> you use `values` these problems go away… except that to get the odd-
> index elements from it you have to `reverse` the array, at which point
> a not-especially-golfed grep is shorter.

So you'd want

values%{{1,@a}}

then

Aristotle Pagaltzis

unread,
May 18, 2012, 5:29:19 AM5/18/12
to f...@perl.org
* Steve Fink <sph...@gmail.com> [2012-05-18 10:25]:
> On Thu, May 17, 2012 at 3:14 AM, Aristotle Pagaltzis <paga...@gmx.de> wrote:
> > * Mike Erickson <m...@quidquam.com> [2012-05-16 15:45]:
> > > If you don't care about order, but just want those elements, you
> > > can also do:
> > >
> > > keys%{{@a}}
> >
> > There is more than order that gets lost. If you use `keys` you also
> > get everything back stringified – undefs are lost and references
> > break. If you use `values` these problems go away… except that to
> > get the odd-index elements from it you have to `reverse` the array,
> > at which point a not-especially-golfed grep is shorter.
>
> So you'd want
>
> values%{{1,@a}}
>
> then

D’oh!

John Douglas Porter

unread,
May 18, 2012, 9:17:47 AM5/18/12
to f...@perl.org, Aristotle Pagaltzis

> From: Aristotle Pagaltzis <paga...@gmx.de>


> Subject: Re: seeking golfing advice
> To: f...@perl.org

> Date: Friday, May 18, 2012, 5:29 AM


D::oh is right. You get an unwanted extra undef at the end of such list.


Smylers

unread,
May 19, 2012, 6:19:17 AM5/19/12
to f...@perl.org
Aristotle Pagaltzis writes:

> And if you have an array to work with in the first place, you can also
>
> @array[map 1+$_*2, 0..$#array/2]
>
> which is 3 characters longer than the grep version

I think that actually gives you the even elements. For the odd elements
(which are even array indices, in Perl's terms) you don't need the C<
1+> in there, saving 3 characters.

@array[map$_*2,0..$#array/2]

The shortest variant like that I've found for even characters is:

@array[map$_*2-1,1..@array/2]

@array is one character shorter than $#array, but to avoid getting an
unwanted undef element when @array has an even number of elements in it
you need to start at 1 rather than 0, which makes the subtraction
necessary.

Smylers
--
http://twitter.com/Smylers2
0 new messages