http://qs321.pair.com?node_id=387056

JAMBOID has asked for the wisdom of the Perl Monks concerning the following question:

Howdi to all. I was thinking something like the sql IN function would be convenient. I was about to write one but I don't know how to access the lvalue to a function. For example:
if ( $x in ( 1, 3, 7 ) ) { # do this }
How does one do this, or is there already some built in function or construct of perl that does this?
Thanks very much,
-jim

Replies are listed 'Best First'.
Re: writing perl function like sql IN
by dave_the_m (Monsignor) on Aug 30, 2004 at 22:57 UTC
    if (grep $x == $_, 1, 3, 7) { ... }

    Dave.

      A slightly more efficient solution (see the discussion below) would use the standard List::Util::first function:
      if (defined first { $x == $_ } 1, 3, 7) { ... }
      Note that it is necessary to use defined(), as first() returns the first matching element (which may be zero), not a list.
Re: writing perl function like sql IN
by Aristotle (Chancellor) on Aug 31, 2004 at 00:47 UTC

    The canonical answer is to use a hash:

    my %in_set; undef @in_set{1, 3, 7}; if( exists $in_set{ $x } ) { # ... }

    The grep solution mentioned before is also common.

    The other solutions posted involving modules have much more overhead than either of these two, but have strengths in various areas beyond your simple use case.

    Makeshifts last the longest.

      grep may be a common solution, but it's not necessarily the most efficient. It probably doesn't matter for small sets or infrequent tests. But if efficiency does matter, one must keep in mind that the grep solution always has an O(n) outcome, even if the item you're looking for is the first element found. grep always searches the entire list.

      Any linear search solution where the loop terminates as soon as the item is found will have a worst case of O(n), but an average case of O(n/2) (which isn't really true big-oh notation)...assuming randomly ordered list.

      A binary search solution will achieve O(log N), again, assuming that the binary search quits as soon as the item is found. That's an order of magnitude faster than the linear search solution.

      A hashing algorithm, well written, such as Perl's %hash can achieve O(1), and is not sensitive to order nor should it be affected by dataset size (see the O'Reilly Wolf book, Mastering Algorithms with Perl, pp. 158-159, 168). So for many purposes a good old fashioned Perl hash might be one of the best solutions. ...assuming the dataset is in memory.

      For those unfamiliar with big-oh (O(x)) notation, it's often used as an analysis of order of growth of work an algorithm must do as a dataset grows. "O(1)" means no growth. "O(n)" means a one-to-one growth ratio. "O(log n)" is an order of magnitude slower growth than "O(n)", for example. The reason that I said O(n/2) for average case was not really valid big-oh notation is because big-oh isn't concerned with average cases. Big-O notation represents an "order of growth won't be worse than..." relationship. Just a quick refresher. ;)


      Dave

        You forget to mention the other side of the trade-off: a hash trades memory for speed. grep will run in O(N), but also consume a lot less memory. It also requires a certain amount of work to set up the hash; if you're only going to test a particular set few times, it may well end up not amortizing that up front effort.

        TANSTAAFL.

        Makeshifts last the longest.

Re: writing perl function like sql IN
by DamnDirtyApe (Curate) on Aug 30, 2004 at 22:50 UTC

    Set::Scalar does this nicely.

    #! /usr/bin/perl use strict; use Set::Scalar; my $x = 3; my $s = Set::Scalar->new( 1, 3, 7 ); if ( $s->has( $x ) ) { print "Found $x\n"; }

    _______________
    DamnDirtyApe
    Those who know that they are profound strive for clarity. Those who
    would like to seem profound to the crowd strive for obscurity.
                --Friedrich Nietzsche
Re: writing perl function like sql IN
by blokhead (Monsignor) on Aug 31, 2004 at 00:26 UTC
    Quantum::Superpositions might give the most readable way to do this:
    use Quantum::Superpositions; if ( $x == any(1,3,7) ) { ... }
    A hash is better than an array or quickly checking of whether an item is included. If you can keep the set of candidates around as a hash with candidates as keys instead of an array of candidates, you can check whether a key is one of the candidates with exists.

    blokhead

Re: writing perl function like sql IN
by jZed (Prior) on Aug 31, 2004 at 00:20 UTC
    use Quantum::Superpositions; my $x = 7; print "ok!\n" if $x == any(1,3,7);
Re: writing perl function like sql IN
by kesterkester (Hermit) on Aug 30, 2004 at 22:57 UTC
    The PDL modules have something like this: perl -MPDL -e '$in = pdl 0..100; $x = 10; print where ( $in, $in == $x )', or you could roll your own with something like this:

    #!/usr/local/bin/perl use warnings; use strict; sub in { my ( $x, @args ) = @_; foreach ( @args ) { return 1 if $_ == $x; } return 0; } print ( "\nyes: ", in (10, 8..12) ); print ( "\nno: ", in (10, 11..120) )
Re: writing perl function like sql IN
by Arunbear (Prior) on Aug 31, 2004 at 12:10 UTC
    use strict; sub in (@) { my $x = shift; $_ == $x and return 1 for @_; 0 } my $x = 10; if(in $x => 1 , 3, 7) { print "Found\n"; } else { print "Not found\n" }
Re: writing perl function like sql IN
by Anonymous Monk on Aug 31, 2004 at 14:19 UTC
    Thanks very much to everyone!
Re: writing perl function like sql IN
by Anonymous Monk on Aug 31, 2004 at 21:37 UTC
    if (grep { $x == $_ } ( 1, 3, 7 ) { # do this }
    Obviously, you would use the string comparison operators if you were locating strings.

    This is also handy with the match operator ($x =~ m/foo/).

    Try 'perldoc -f grep' if you want more.