Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Condensing a grep into a sort

by seaver (Pilgrim)
on Jun 09, 2009 at 17:01 UTC ( #770013=perlquestion: print w/replies, xml ) Need Help??
seaver has asked for the wisdom of the Perl Monks concerning the following question:

Dear all,

I wrote this code, and I was challenging myself to condense the code. Basically, I was trying to figure out a way to use regular expressions within the sort in both list and scalar context, and thus not use the grep function.

The catch is, I wanted to do it with less characters than you would find in the grep line.

Can anyone help me?
Sam
my @compounds=("C3H8O2", "C6H10O5", "C36H62O31", "C5H16N2", "C10H11N5O6P", "C9H11N3O7P", "C3H9N2O2"); print join("\n", sort{($a =~ /C(\d*)/)[0] <=> ($b =~ /C(\d*)/)[0]} grep { $_ =~ /P/} @compounds),"\n";

Replies are listed 'Best First'.
Re: Condensing a grep into a sort
by Roy Johnson (Monsignor) on Jun 09, 2009 at 17:26 UTC
    It is not reasonable to have a sort that excludes members of the list entirely. Sort rearranges lists; grep filters them.

    What you might want to do is a Schwartzian Transform so that the regex doesn't need to be applied to $a and $b repeatedly.

    print map {"$_->[0]\n"} sort {$a->[1] <=> $b->[1]} map { [$_, (/C(\d*)/)[0]] } grep { /P/} @compounds;
    Hoping for partial credit, I eliminated the join.

    Caution: Contents may have been coded under pressure.
Re: Condensing a grep into a sort
by jwkrahn (Monsignor) on Jun 09, 2009 at 17:25 UTC

    I would write that more simply as:

    my @compounds = qw( C3H8O2 C6H10O5 C36H62O31 C5H16N2 C10H11N5O6P C9H11N3O7P C3H9N2O2 ); print map "$_\n", sort { ( $a =~ /C(\d+)/ )[ 0 ] <=> ( $b =~ /C(\d+)/ )[ 0 ] } grep /P/, @compounds;

    And you could also use a ST as well:

    my @compounds = qw( C3H8O2 C6H10O5 C36H62O31 C5H16N2 C10H11N5O6P C9H11N3O7P C3H9N2O2 ); print map "$_->[0]\n", sort { $a->[ 1 ] <=> $b->[ 1 ] } map /P/ ? [ $_, /C(\d+)/ ] : (), @compounds;
Re: Condensing a grep into a sort
by gwadej (Chaplain) on Jun 09, 2009 at 17:33 UTC

    I'm unsure of what you mean. grep is used to filter a list. sort is used to change the order of a list. Except for some very unusual definitions of sort it should never change the size of the list.

    You are asking how to condense code, but not really telling what you are trying to accomplish. It is quite possible that there is a simpler/more concise solution, if we knew the problem, not your current solution.

    As for improving your solution, I would probably apply the Schwartzian transform to reduce the number of regex applications, but that won't be shorter.

    my @compounds=("C3H8O2", "C6H10O5", "C36H62O31", "C5H16N2", "C10H11N5O6P", "C9H11N3O7P", "C3H9N2O2"); print join("\n", map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [ $_, (/C(\d+)/)[0] } grep { $_ =~ /P/} @compounds ),"\n";
    G. Wade

      I do see your point (and Johnson's) in that you shouldn't combine grep and sort.

      I was writing this code as an example of how you can pass along modified lists without having to create temporary arrays etc. A friend wrote the same in Python (albeit, in a more ugly way, IMO) and I was sure I could emphasise Perl's flexibility by getting the same solution with less code.

      I was wondering really, if it was possible to have a regular expression return 'null', in list context, and to have sort somehow not add the null to the sorted list. Does this make sense?

      What I'm trying to achieve here, is simply to print out the molecules that have a phosphorus element ('P'), in the order of the number of carbons within the molecule.

        I was wondering really, if it was possible to have a regular expression return 'null', in list context, and to have sort somehow not add the null to the sorted list. Does this make sense?

        I presume you mean the match operator when you say regular expression.

        And yes, that's possible. The match operator returns an empty list in list context for a failed match.

        $ perl -le'print for map /^foo(.*)/, @ARGV' foobar foobaz dodo bar baz

        The problem isn't what it returns when the match fails, it's what it returns when the match succeeds. You want it to return both the number and the entire string as one value, and that's not going to happen.

        I was wondering really, if it was possible to have a regular expression return 'null', in list context,

        Yes, a regular expression will return 'null' in list context:

        $ perl -le' use Data::Dumper; for ( "C123xyz", "Cklm" ) { push @data, /C(\d*)/, "MARKER"; } print Dumper \@data; ' $VAR1 = [ '123', 'MARKER', '', 'MARKER' ];
        and to have sort somehow not add the null to the sorted list.

        sort does not provide a way to change the size of the list it operates on, so no.

Re: Condensing a grep into a sort
by Marshall (Abbot) on Jun 09, 2009 at 19:40 UTC
    Your code is clever, and perhaps a bit too clever in the sort{}. This $a =~ /C(\d*)/)[0] generates an intermediate var with the result of the slice. That var will be generated even if we don't give it a name. I would argue to give it a name as that has no run time consequence.

    To answer your question re: grep line, see below...just one or 2 chars. /P/ or /P$/. The $ gives the regex engine a clue that the last letter must be 'P'. This construct will run very quickly.

    I give kudos to your algorithm. Most of the lines don't have things of interest and you filter to get the lines that are of interest (the ones that end in P). There are fancy sort methods, but if you can select only the lines that have meaning, there is less to sort!

    I think something straightforward like this will run well.

    #!/usr/bin/perl -w use strict; my @compounds=("C3H8O2", "C6H10O5", "C36H62O31", "C5H16N2", "C10H11N5O6P", "C9H11N3O7P", "C3H9N2O2"); print join("\n", sort{ my ($num_A) = ($a =~ /^C(\d+)/); my ($num_B) = ($b =~ /^C(\d+)/); $num_A <=> $num_B } grep { /P$/ } #could be just /P/? @compounds),"\n"; __END__ C9H11N3O7P C10H11N5O6P

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://770013]
Approved by McDarren
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others imbibing at the Monastery: (5)
As of 2018-10-18 16:52 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    When I need money for a bigger acquisition, I usually ...














    Results (103 votes). Check out past polls.

    Notices?