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

Range Operator in If Statement

by mmartin (Monk)
on Jan 04, 2012 at 20:26 UTC ( #946284=perlquestion: print w/ replies, xml ) Need Help??
mmartin has asked for the wisdom of the Perl Monks concerning the following question:

Hey Monks,

My Version:
Perl v5.8.8

I have a pretty simple question if someone could help me with...?

How do I use the range operator in an if statement (or is it possible) on a variable that is a number?

For Example:

my $ref = 7; if ($ref = 1 .. 8) { ...code.... }
In other words say, if $ref is between 1 and 8 then do stuff...
I've tried the above code in like 20 different ways but keep getting errors.

I've tried:
if ($ref =~ 1..8) if ($ref = 1..8) if ($ref == 1 .. 8) if ($ref =~ (1 .. 8)) etc...


Any thoughts would be great!


Thanks in Advance,
Matt


.

Comment on Range Operator in If Statement
Select or Download Code
Re: Range Operator in If Statement
by Perlbotics (Abbot) on Jan 04, 2012 at 20:45 UTC

    If you insist in using a range-/list-op, you could do:

    if ( grep { $ref == $_ } 1..8 ) { ... }
    But personally, I would rather use something like
    if ( $ref >= 1 and $ref <= 8 ) { ... }
    or maybe
    if ( $ref =~ /^[1-8]$/ ) { ... }

    Update: (in response to OP's reply below) With a modern Perl, you could use given/when:

    use v5.10; use strict; use warnings; my @tests = qw(1 9 17 25 33); my $range_25_32 = [25..32]; # alternatve, maybe(?) faster for my $ref ( @tests ) { given ( $ref ) { when ( [1..8] ) { say "A $ref" } when ( [9..16] ) { say "B $ref" } when ( [17..24] ) { say "C $ref" } when ( $range_25_32 ) { say "D $ref" } default { say "ELSE $ref" } } }
    Output:
    A 1 B 9 C 17 D 25 ELSE 33
    With the given ranges in the example below, some bitwise operators might also work.
      Hey perlbotics, thanks for you QUICK reply...

      Ohh ok I thought I could use the range operator for this type of thing, but guess not.

      I wanted to use that because it seemed like it was the "shorthand" way of doing it.
      Wanted a short way because I needed a whole bunch of those statements and I didn't want to have a ton of these statements:
      if ($ref >= 1 && $ref <= 8) { ...... } if ($ref >= 9 && $ref <= 16) { ...... } if ($ref >= 17 && $ref <= 24) { ...... } if ($ref >= 25 && $ref <= 32) { ...... } #....more checks

      But if that's what you recommend then I'll just use my original way I had like above and like you had...

      Thanks again for the reply,
      Matt
        This looks like a power of 2 problem. If you could give us some more detail about what you are doing, I think some very efficient solutions might be forthcoming.
        When classifying by intervals, better try $case = int( ($ref-1) / 8 ) , so you only have to test for the results 0,1,2,3...

        DB<104> for $ref (1,8,9,16,17,24,25,32) { print "$ref: ", int(($ref-1) +/8), "\n" } 1: 0 8: 0 9: 1 16: 1 17: 2 24: 2 25: 3 32: 3

        Cheers Rolf

        If you have multiple consecutive intervals, it's better to use an elsif cascade, such as

        if ($ref <= 8) { say "ref is at most 8."; } elsif ($ref <= 16) { say "ref is above 8 but at most 16." } elsif ($ref <= 24) { say "ref is above 16 but at most 24." } elsif ($ref <= 32) { say "ref is above 24 but at most 32." } else { sat "ref is above 32." }
        The elsif part means that if the previous condition was found true, the next one is not tested for, so eg. if $ref = 5 then the first branch is executed, the rest of the tests and branches are skipped.
Re: Range Operator in If Statement
by toolic (Chancellor) on Jan 04, 2012 at 20:53 UTC
    ...or, you could reuse an existing function from Acme::Tools (or create your own function)
    use warnings; use strict; use Acme::Tools qw(between); my $ref = 7; if (between($ref, 1, 8)) { print "between\n"; }
      toolic, thanks for the reply...

      Sweet deal... I just cpan-ed it and I will give it a try.


      Thanks alot,
      Matt
Re: Range Operator in If Statement
by kejohm (Hermit) on Jan 04, 2012 at 21:35 UTC

    If you are able to upgrade to a newer version of Perl, starting with v5.10, you can use the smart match operator, eg:

    use 5.010; my $ref = 7; if ( $ref ~~ [ 1 .. 8 ] ) { ... }
Re: Range Operator in If Statement
by ww (Bishop) on Jan 04, 2012 at 22:12 UTC
    Another way, perhaps even related to the code in OP:
    #!/usr/bin/perl use Modern::Perl; # 946284 my $ref = 7; for (1 .. 8 ) { if ($ref == $_) { say "\$ref: $ref"; # adjust output to id source of match? } else { say "Tain't workin'"; } }

    Seems to me this might be readily expansible to your larger goal as outlined in Re^2: Range Operator in If Statement

Re: Range Operator in If Statement
by LanX (Canon) on Jan 05, 2012 at 01:40 UTC
    Just to clarify why your tests don't throw errors:

    When you're using the .. range operator in scalar context, you are actually getting the flip-flop behavior!

    see Range Operators

    I recommend combining two <= operators with &&, testing for inclusion after generating temporary lists is too much overhead. There is nothing closer to Python's 1 <= x <= 8 in Perl.

    HTH

    Cheers Rolf

Re: Range Operator in If Statement
by NetWallah (Abbot) on Jan 05, 2012 at 04:52 UTC
    In the TIMTOWTDI tradition, I offer Quantum::Superpositions (untested):
    use Quantum::Superpositions; if ( $ref == any(1..8) ) {

                "Battle not with trolls, lest ye become a troll; and if you gaze into the Internet, the Internet gazes also into you."
            -Friedrich Nietzsche: A Dynamic Translation

Re: Range Operator in If Statement
by TJPride (Pilgrim) on Jan 05, 2012 at 05:29 UTC
    If the ranges you're matching on are consecutive, you could do something like this:

    use strict; use warnings; for (-4, 1, 7, 12, 89, 54, 13, 104) { if ($_ < 1 || $_ > 64) { print "$_ outside.\n" } elsif ($_ <= 8) { print "$_ 1-8\n"; } elsif ($_ <= 16) { print "$_ 9-16\n"; } elsif ($_ <= 32) { print "$_ 17-32\n"; } elsif ($_ <= 64) { print "$_ 33-64\n"; } }
Re: Range Operator in If Statement
by GrandFather (Cardinal) on Jan 05, 2012 at 06:26 UTC

    You've got a bunch of answers to the question you asked, but (as suggested in one answer) it looks like we could give you a better answer if we knew what you are trying to do. There may be a nifty trick we could tell you about if you really are doing something related to powers of 2 or something of that sort so how about giving us a little more back story to work with?

    True laziness is hard work
Re: Range Operator in If Statement
by mmartin (Monk) on Jan 05, 2012 at 15:14 UTC
    Wow, thanks everybody, what a response..!!

    kejohm,
    In terms of updating to Perl 5.10 and using the Smart Match Operator. I did look into that but updating the Perl version wasn't really an option at the moment bc it is a production server and a bunch of things running depend on that. But I did want to try to use that at some point...


    ww,
    Thanks for the reply, The example you show is sort of what I was trying to achieve with the ranges, maybe I'll give that one a try as well. Thanks again!


    LanX,
    Oh ok I gotcha... Bc it was working just not in the way I thought it would...? Thanks for the info.


    NetWallah,
    Hey, that looks cool I'll have to look into that Module as well. Seems alot like the Acme::Tools Module that I eventually went with from toolic's suggestion... Thanks I'll have to download that Module.


    TJPride,
    That's a good one..! I like that! Don't know why I didn't think of that. Clean and simple, since the control structure, once it finds a match, it will drop out of the if/elsif clause which leads to not having to use a range to test on, but just one test instead... Cool thanks..!


    GrandFather,
    Hey thanks for the reply. Sorry it took me a while to respond back but I decided to go with toolic's suggestion with the ACME::Tools solution... Works Good!
    But in terms of a backstory. The thing I need this for is a script to run along side with MRTG and Interafce Templates to check which GigabitEthernet port it is and then separate them into groups to test for Backplane usage on a Cisco switch. Backplane is Bundeled ports (usually 8 consecutive one's) ranging from (Gi3/1<-->Gi3/48; Gi4/1<-->Gi4/48; ... all the way to ... Gi7/1<-->Gi7/48).

    So basically some of the groups would be in ranges as follows... For example the range Gi3/1..Gi3/8 would be "Bundle-Gi3A", and Gi3/9..Gi3/16 would be "Bundle-Gi3B" all the way up to Bundle-Gi3F. As for Gi4 -to- Gi7 it would be the same Letters only difference would be the Gi number...


    Here is my entire solution to the original question:
    The original question had to do with the Subroutine "get_bundle()"
    I know it's a SIMPLE program but here it is. Basically you just pass it the interface name (i.e. "Gi3/7") broken into two pieces, (1) the Gi number "Gi3" and (2) the reference number "7", which is passed from an MRTG Template/Script and then passed back to the Template to be added to the config ("cfg") file.

    #!/usr/bin/perl use warnings; use strict; use Getopt::Long; use Acme::Tools qw(between); # Declare Command Line Option's Variables my $Gi_number; my $ref_number; my $get_what; # Declare regular variables my $bundle; my $target_lines; #my $target_name = "target_name"; my @number; my $GInum; my $userGraph; # Process Command-Line Options with the GetOptions::Long Module if ( @ARGV > 0 ) { GetOptions('Gi_number=s' => \$Gi_number, 'ref_number=s' => \$ref_number); } ### Extract the 'Gi' Number (i.e. Gi4 --> '4') and set it to $GInum @number = ($Gi_number =~ /(\d)/); $GInum = $number[0]; &get_bundle(); ### Build the the UserDefined Graph Name for the routers.cgi*Graph[] L +ine $userGraph = "Bundle-$Gi_number$bundle"; if ($bundle) { $target_lines .= "routers.cgi*Graph[\$target_name]: $userGraph tot +al average active\n"; } else { print "WARNING: No Bundle Letter has been assigned to \$bundle +... Try again.\n"; exit 2; } ###################################################################### +############## # SUB - get_bundle() --- This sub will take the interface reference nu +mber Gi3/"7" # # which is the '7' and get the corresponding Bundle letter +s.# ###################################################################### +############## sub get_bundle() { if (between($ref_number, 1, 8)) { $bundle = "A"; } if (between($ref_number, 9, 16)) { $bundle = "B"; } if (between($ref_number, 17, 24)) { $bundle = "C"; } if (between($ref_number, 25, 32)) { $bundle = "D"; } if (between($ref_number, 33, 40)) { $bundle = "E"; } if (between($ref_number, 41, 48)) { $bundle = "F"; } } ###################################################################### +########### ### END: get_bundle() print "$target_lines";

    I'm SURE there is a 100 different ways to do this, but this works for my current situation...

    I REALLLY want to thank all of you for your suggestions, it was MUCH MUCH more then I expected..!!

    Thanks Again Everybody,
    Matt


      I'm SURE there is a 100 different ways to do this...

      Here's one way to assign to $bundle that requires a lot less typing. Replace your call to get_bundle and the sub itself with:

      $bundle = [ 'A' .. 'F' ]->[ ( $ref_number - 1 ) / 8 ];

      Update: Removed unnecessary parentheses around the arrayref.

        Not_a_Number, thanks for the reply...

        Cool stuff... Could you explain a little how that works exactly? I'm still relatively new to Perl.


        Thanks,
        Matt


Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others lurking in the Monastery: (11)
As of 2014-07-30 03:14 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:









    Results (229 votes), past polls