Re: lsearch for perl?
by ikegami (Patriarch) on Dec 05, 2005 at 15:45 UTC
|
$found = 0;
@list = grep { $item ne $_ || $found++ } @list;
or
my ($index) = grep { $item eq $list[$_] } 0..$#list;
splice(@list, $index, 1) if defined $index;
or
use List::Util qw( first );
my $index = first { $item eq $list[$_] } 0..$#list;
splice(@list, $index, 1) if defined $index;
Update: Tested. Fixed flawed grep rule.
| [reply] [d/l] [select] |
|
use List::MoreUtils qw( first_index );
my $index = first_index { $item eq $_ } @list;
# Returns -1 if no such item could be found.
| [reply] [d/l] |
|
Thanks to all for your replies - they were all very helpful.
Things like this
@list = grep { $item ne $_ } @list;
aren't in the man-page, and got me thinking. This however:
my ($index) = grep { $item eq $list[$_] } 0..$#list;
splice(@list, $index, 1) if defined $index;
was a real satori, Keanu-Reeves-like "Whoah!!" moment for me. The idea of using grep on a list without naming _that_ list as the argument to grep would never have occurred to me. Guess I was stuck in Unix grep thinking!
Feeling quite humbled. | [reply] [d/l] [select] |
|
Things like this aren't in the man-page
grep accepts any code in the block. It doesn't have to be a regex. grep is useful for filtering out items from a list, while map is useful for transforming a list.
The idea of using grep on a list without naming _that_ list as the argument to grep would never have occurred to me.
It's an concept I picked up on PerlMonks too :)
| [reply] [d/l] [select] |
Re: lsearch for perl?
by Zaxo (Archbishop) on Dec 05, 2005 at 15:46 UTC
|
Sure, grep will simplify that, with a minor change in behavior - maybe for the better.
sub lsearch {
my $item = shift;
grep { $item eq $_[$_] } 0 .. $#_;
}
That will return the empty list for false, and otherwise a list of each index that succeeds
Usage becomes, for your example,
for (reverse lsearch $item, @list) {
splice @list, $_, 1
}
# or
splice @list, $_, 1 for reverse lsearch $item, @list;
so all instances of $item are removed from the array. The reverse call is needed to preserve the indexes from being shifted out of sync by splice.
An alternative is to define a winnow() function to do it all:
sub winnow {
my $item = shift;
grep { not $item eq $_ } @_;
}
@list = winnow $item, @list;
That is maybe less flexible, but I think cleaner.
| [reply] [d/l] [select] |
Re: lsearch for perl?
by VSarkiss (Monsignor) on Dec 05, 2005 at 16:16 UTC
|
You have some excellent examples above of how to achieve what you asked for. But I want to address your second point:
Or do you just program to avoid such cases?
That's really the heart of the matter. In Perl, it's much more convenient to iterate over array or list items directly, rather than trying to refer to them by index. If you find yourself writing:
for my $i (0 .. $#array) {
if ($array[$i] eq ... ) {
immediately stop and change that to:for my $i (@array) {
if ($i eq ... ) {
In the long run, it'll help you to avoid off-by-one problems as well.
Of course, there are situations where you really do need array indexes, but that need is much rarer in Perl than in, say, C, or Tcl, or similar languages.
| [reply] [d/l] [select] |
Re: lsearch for perl?
by pileofrogs (Priest) on Dec 05, 2005 at 16:59 UTC
|
While this isn't really an answer to the "lsearch" question, it does address the "perl mentality" part of the question
When I first started using perl (after some time in C) I thought hashes were black magic and I did a lot of stuff with lists that I would now do with a hash. In fact, I use lists pretty rarely any more.
When you're using lists, ask yourself if it could be done more easily with a hash.
-Pileofrogs
Updated: Yes! Thank you thor and ikegami. I think that was what I was trying to say.
| [reply] |
|
When you're using lists, ask yourself if it could be done more easily with a hash.
In my opinion, they address different problems. Arrays are an ordered set of things, hashes are an unordered set of associations. Sure, if you find yourself doing something like this:
my (@item, @color);
while(<DATA>) {
chomp;
my($item, $color) = split(/,/);
push @item, $item;
push @color, $color;
}
for(my $i=0; $i < @item; $i++) {
print "$item[$i] has color $color[$i]\n";
}
__DATA__
apple,red
orange,orange
banana,yellow
then yes...use a hash. Don't try to shoehorn it, though.
thor
The only easy day was yesterday
| [reply] [d/l] |
|
| [reply] [d/l] [select] |
|
|
|
Arrays have many uses and sometimes perl programmers can overuse hashes, but as an ex-Tcl programmer myself I can give the advice to:
- use hashes when you can
- when an array is appropriate, use foreach, map and grep. Some perl programmers might overuse map and grep, but when they are good they are VERY good!
- if you find yourself using an array index ask yourself why - there is almost always a better way in perl
For the enlightenment of non-Tcl people, Tcl has only two types of data. Strings and lists. That's it. Even function calls are just a list where the first list value is the name (string) of a function (or proc) and the rest of the list values are the arguments (which may be, you guessed it, strings or lists).
The lists aren't even very convenient, vis. lsearch.
Don't think I'm bashing Tcl - it's very useful when you need a straightforward embeddable scripting language. You can even make it do very non-trivial things (vis. OpenACS) but it hurts.
upvar anyone?
| [reply] |
Re: lsearch for perl?
by jbullock35 (Hermit) on Dec 05, 2005 at 19:50 UTC
|
| [reply] |
|
| [reply] |