in reply to Idiomatic Array Index Search?

I think your code is fine. arOn's code is brief and certainly idiomatic. This is a little more self documenting and what I might use, depending on my mood as much as anything else! See below for the one liner.

sub getArrayIndex { my $value = shift; for my $index(0..$#_) { return $index if $value eq $_[$index]; } return -1; }

The loop structure:

for ($i=0,$i<$x,$i++)
is available and consistent with many other languagues but in Perl these idioms are shorter and sweeter:
# to just iterate over an array; each element assigned to $_ # changing $_ changes the original array element for (@array) { # assigning to $_ here will change original array # comparing to $_ of course will not } # to iterate over a range for (0..10) { # $_ will be set to 0 to 10 in sequence } # to iterate over a range with a named op for $i(0..10) { # $i will be set to 0 to 10 in sequence }
With the iterate over a range options note that $#array (returns the last index) whereas @array (in scalar context) return a scalar corresponding to the length of the array so that:
for (0..$#array) { } for (0..@array) { }

are *almost* equivalent. This originally read *are equivalent* which is absolutely wrong. As dvergin points out because @array returns the number of elements in @array the second example will actually access an undefined array element one past the real end of the array. OK so it still usually works but consider this code:

@array = qw(1); print "My array @array\n"; for (0..10) { for (0..@array) {$array[$_]++} } print "My array @array\n";

If you run this you will discover a potentially nasty suprise.

My array 1 My array 12 11 10 9 8 7 6 5 4 3 2 1

Auto-vivification means that in perl if you operate on a variable which does not exist then perl just creates it for you. As a result each time through the inner loop we create a new array element. As a result our initial one element array consists of twelve elements at the end. Nice neat memory leak there. Neither use strict nor warnings catch this either.

In perl there are some outrageously idiomatic (read short and often difficult to understand) ways to do things, and while it is fun to play golf (make really short idiomatic code to do a task) it is not the best practice if you have to maintain the code in the future. There is a happy medium there somewhere but as with all things beauty is in the eye of the beholder.

A "look how clever I am :p" really idiomatic solution

# really short idiomatic version sub getArrayIndex { map{return --$_ if $_[0] eq $_[$_]}(1..@_-1) and -1 } # test code @array = qw(0 2 4 6 8 10 12); print getArrayIndex(6,@array),"\n"; print getArrayIndex(5,@array),"\n";

I think this is about as idiomatic as you can get. It performs the task, but is not perhaps as easy to understand! I will post an explanation if you want - send a chat message.

tachyon

nysus wanted an explanation of this GOLF from Idiomatic Array Index Search?

sub getArrayIndex {
      map{return --$_ if $_[0] eq $_[$_]}(1..@_-1) and -1
}

# First you need to know that a sub returns the last value it evals
# Next you need to know that a function will generally return true
# Next you need to know that the part after an and is only evaluated
# if the first part evals true.
# Next you need to understand that map{}@array is essentially the same as 
# for (@array) {} - it just uses two less chars (the parenths around @array)

# In this case the function is passed two args - the value to search for
# and the array to search for it in. These args appear in @_ so @_[0]
# more correctly written as $_[0] is the search value. Everything
# else in @_ is the search array.

# So what we do is iterate from 1 to the end of the array. As you probably 
# know the last index of an @array is given by $#array. The number of 
# elements is @array (in scalar context) so the last index is @array-1
# By now you should see that 1..@_-1 is therfore a range from 1 to our
# last index in @_. As a result our map iterates from 1..@_-1 assigning
# the index value to $_

# At this point it is important to remember that element 0 of our search
# array (in the real world) is actually at index 1 (in our sub) because 
# the first element in @_ is our search value, thus causing an offset.
# So when we find a match the actual index will be ($_ - 1).
# Now --$_ just happens to be $_ -1 so this is what we return when we find
# that [0] eq $_[$_]. Note --$_ is required as we need
# this to occur *before* the return so we return this. $_-- fails to work.
 
# If we fail to return out of the map, eventually it finishes and evals to
# true so the part on the right hand side of the and gets evaluated. -1 
# evals to uhm ah -1 so the last value evaluated by the sub is -1 and 
# this is what it returns.

# ultra GOLF
sub getArrayIndex {
    map{return --$_ if[0]eq$_[$_]}1..@_-1and-1
}

# different style, shaves a few chars
sub getArrayIndex {
    $i--;map{$i=$_-1if[0]eq$_[$_]}1..$#_;$i
}

# shave a few more by being sneaky
sub getArrayIndex {
    map{$i=$_ if[0]eq$_[$_]}1..$#_;--$i
}

# less GOLF, using for and an unnecessary $i insead of $_
sub getArrayIndex {
     for $i(1..$#_) {
        return ($i-1) if [0] eq $_[$i];
     }
    -1
}

# almost production code
sub getArrayIndex {
     $find = shift;
     @array = @_;
     for $index(0..$#array) {
        return $index if $find eq $array[$index]
     }
  return -1 # we return -1 if search fails
}

So thats it. Idiomatic perl with comments!!!! Not as short as baby perl!

Replies are listed 'Best First'.
Re: Re: Idiomatic Array Index Search?
by dvergin (Monsignor) on May 27, 2001 at 07:52 UTC
    Terrific write-up tachyon. Just one small (though crucial) quibble about the difference between '$#array' and 'scalar @array':
    my @array = qw(a b c d e f); for (0..$#array) {print $_} # zero thru last index for (0..@array) {print $_} # zero thru size of array
    prints: '0123450123456'

      Ah... good point. Perls auto vivification has obviously let me silently access one element past the end of the real array in that example. I normally use $#array but recently had to use @_ as $#_ was causing a syntax error (it was wickedly twisted code). I had thus far *failed* to note the difference.

      Thanks very much. I have added a correction to previous post with appropriate credit of course.

      On more pearl of wisdom duly filed

      tachyon