4

Because I can't find a convenient way to check if $str is in @array, I'm trying to make one myself, but it is not working.

I guess it is because of the mix-up of array and string. It keeps giving 0 to $str. Please let me know how to fix it.

use 5.010;
use strict;
use warnings;

sub ifin {
    my (@array, $str) = @_;
    for my $i (@array) {
        if ($i eq $str) {
            return 1;
        }
    }
    return 0;
}

my @f = (1, 2, 3, 4);
my $k = 1;
print ifin(@f, $k);
daxim
  • 39,270
  • 4
  • 65
  • 132
Ivan Wang
  • 8,306
  • 14
  • 44
  • 56
  • 1
    Couldn't you get around this by swapping the parameters round, and passing in the string first and then the array? – andrewsi May 17 '12 at 18:11
  • 1
    From the [official Perl FAQ](http://faq.perl.org): [How can I tell whether a certain element is contained in a list or array?](http://learn.perl.org/faq/perlfaq4.html#How-can-I-tell-whether-a-certain-element-is-contained-in-a-list-or-array-) – daxim May 17 '12 at 18:12
  • Previously on Stack Overflow: http://stackoverflow.com/questions/720482/how-can-i-verify-that-a-value-is-present-in-an-array-list-in-perl http://stackoverflow.com/questions/4571046/perl-how-to-check-if-any-integers-in-a-list-of-integers-are-in-an-array http://stackoverflow.com/questions/6942082/faster-way-to-check-for-element-in-array – daxim May 17 '12 at 18:17

5 Answers5

7

You can't pass arrays to subs, only lists of scalars.

ifin(@f, $k);

is the same as

ifin($f[0], $f[1], $f[2], $f[3], $k);

because @f evaluates to a list of its elements.

One way of passing an array to a sub is to pass a reference.

sub ifin {
   my ($array, $str) = @_;
   for my $e (@$array) {
      return 1 if $e eq $str;
   }

   return 0;
}

my @f = (1,2,3,4);
my $k = 1;
print(ifin(\@f, $k), "\n");

By the way, that can also be written as:

my @f = (1,2,3,4);
my $k = 1;
print(( grep { $_ eq $k } @f ) ? 1 : 0, "\n");

You could keep the existing calling convention by using pop.

sub ifin {
   my $str = pop(@_);
   for my $e (@_) {
      return 1 if $e eq $str;
   }

   return 0;
}

my @f = (1,2,3,4);
my $k = 1;
print(ifin(@f, $k), "\n");
ikegami
  • 367,544
  • 15
  • 269
  • 518
3

You may want to check any in the List::MoreUtils package, you just use it like:

use List::MoreUtils 'any';

my @f= qw(1 2 3 4);
my $k=10;

print "yes\n" if( any { $_ == $k } @f );

check the documentation in:

perldoc List::MoreUtils.
quicoju
  • 1,661
  • 11
  • 15
2

How about $str ~~ @arr in a smartmatch? That's available in Perl 5.10.

use 5.010;
use strict;
use warnings;

my $str = 'three';
my @arr = qw(zero one two three four);
my @badarr = qw(zero one two four eight);

say '$str ', $str ~~ @arr? 'is' : 'is not', ' in $arr.';
say '$str ', $str ~~ @badarr? 'is' : 'is not', ' in $badarr.';

Output, as expected:

$str is in $arr.
$str is not in $badarr.
Tom Williams
  • 378
  • 1
  • 5
1

You could pass your arguments in reverse order (print ifin($k, @f);), so array is going last. When you catch them now from subroutine, string comes first and array gets populated with any list items after it.

w.k
  • 8,218
  • 4
  • 32
  • 55
0

You could use a prototype, but those are kind of brittle. I would pass in a reference to @f as the first argument, like this:

use 5.010;
use strict;
use warnings;

sub ifin
{
my ($array,$str)=@_;
 for my $i (@$array)
 {
  if ($i eq $str)
  {
   return True
  }
 }
 return False
}


my @f= (1,2,3,4);
my $k=1;
print ifin(\@f,$k);

For a long list, you avoid making a copy of every list element as well.

Matt K
  • 13,370
  • 2
  • 32
  • 51
  • 1
    Technically, you are still passing a reference even if you have the prototype do the referencing for you :) – ikegami May 17 '12 at 18:08