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

I was using a proprietary scripting language that had two really useful functions:
%pos("string", "t") <--returns position of "t", otherwise null value %ismember(@array, "string") <--returns position of "string" if it exi +sts in @array, otherwise returns null value

I can't seem to find anything analogous to these in Perl. Could someone point me in the right direction please?

Replies are listed 'Best First'.
Re: Returning position?
by GrandFather (Saint) on Feb 24, 2007 at 10:35 UTC

    The equivelent to %pos is index which returns the (0 based) index or -1 if the search string isn't found.

    Update: Second part.

    Perl doesn't provide a built in array lookup returning an index. Perl does however have a lot of ways of manipulating arrays of strings that mean that the lack is not generally a problem. In particular take a look at for, map and grep.


    DWIM is Perl's answer to Gödel
Re: Returning position?
by BrowserUk (Patriarch) on Feb 24, 2007 at 11:15 UTC
Re: Returning position?
by ikegami (Patriarch) on Feb 24, 2007 at 19:27 UTC

    For ismember, have you considered using a hash instead of an array? Then you could do

    my $ismember = exists $hash{string};

    If you're going to do a bunch of lookups, you could convert the array to a hash temporarily.

    my %hash = map { $_ => $_ } @array; print($hash{string1}, "\n"); print($hash{string2}, "\n"); print($hash{string3}, "\n");

    The following returns the index or undefined, but create a list as big as the array in memory.

    use List::Util qw( first ); my $idx = first { $array[$_] eq "string" } 0..$#array;

    A memory efficient version of the above.

    sub ismember { my ($array, $search) = @_; for my $i (0..$#$array) { return $i if $array->[$i] eq $search; } return (); } my $idx = ismember(\@array, "string");

    Update: Applied Not_a_Number's fix. Thanks.

      In your second snippet:

      my $idx = first { $_ eq "string" } 0..$#array;

      You surely meant to write:

      my $idx = first { $array[$_] eq "string" } 0..$#array;

      I meant to mention a binary search would be faster, but that would require @array to be sorted.

Re: Returning position?
by hangon (Deacon) on Feb 26, 2007 at 20:17 UTC

    Just in case this is useful - once upon a time I benchmarked lookup tables. The test looked up every table entry once in random order, then repeated a number of times to get an average. Here's a summary from my notes:

    Comparison of lookup tables Range: 2 to 128 byte strings, 10 to 100,000 values 1) By hash key lookup. 2) By iteration over an unsorted array with foreach, exiting at first match. 3) By search algorithms on a sorted array. Results: 1) For small string sizes, and larger strings with fewer than 1000 table values a hash key lookup ranged from 0 to 33% faster than the unsorted array. 2) With 8 byte or longer strings the speed crossover between hash and unsorted array usually occurred with tables between 1000 and 2000 values. At 2000 values the unsorted array lookup speed was: * 20% faster for 8 character strings * 50% faster for 32 character strings * 100% faster for 128 character strings 3) In all cases the sorted array was significantly slower regardless of search algorithm. Worst case was 4% of hash lookup speed, best case 17% of hash lookup speed.

    This was done in ActivePerl 5.6 on a 1GHz 'doz 98 machine. Moral: in most cases you may be better off using whatever format your data is already in rather than converting between arrays and hashes.

Re: Returning position?
by Moron (Curate) on Feb 26, 2007 at 16:28 UTC
    map is particularly suited to that ismember functionality and undef() returns the Perl equivalent of a null value...
    my ( $result, $count ) = (undef(),0); map ( $result ||= $count if ( $_ eq "string" ); $count++; } @array;
    Update: Note that in Perl, 0 is the first position in an array.

    -M

    Free your mind