Re: Failed array attemp
by tobyink (Canon) on May 13, 2012 at 21:37 UTC
|
push @z, (grep { $_ ~~ \@y } @x) ? 'y' : 'n';
Shifting some of that into a sub might make it a little more readable...
sub arrays_intersect { grep { $_ ~~ $_[1] } @{$_[0]} }
push @z, arrays_intersect(\@x, \@y) ? 'y' : 'n';
Or you could use Set::Scalar...
my $x_set = Set::Scalar->new(@x);
my $y_set = Set::Scalar->new(@y);
push @z, $x_set->intersection($y_set)->empty ? 'n' : 'y';
perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
| [reply] [d/l] [select] |
|
Looks like exactly what I want but does not work. I got rid of syntax error by substituting ~~ with == but the results is wrong.
@x = qw(1 4);
@y = qw(1 2 3);
push @z, (grep { $_ == \@y } @x) ? 'y' : 'n';
print $z[0];
print $z[1];
$z[0] gives me now and $z1 is uninitilised. | [reply] [d/l] |
|
The ~~ was intentional. It's the smart match operator. == won't cut it.
Smart match was introduced in Perl 5.9.3, about 6 years ago.
perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
| [reply] [d/l] [select] |
|
|
|
| [reply] |
|
I keep getting syntax error and can't change the version so it must be old. Sorry to bother but anyway of making this work without smart catching? (can change slightly by I will be using this scheme a lot so I like the one line clean code. Thanks again.
push @z, (grep { $_ ~~ \@y } @x) ? 'y' : 'n';
| [reply] [d/l] |
|
|
Re: Failed array attemp
by stevieb (Canon) on May 13, 2012 at 21:27 UTC
|
imho, the ternary (?:) operator isn't really meant to perform side effects. It was designed to select from two separate values:
$x = (1 == 1)
? 'true'
: 'false'
'true' is put into $x if 1==1, and 'false' otherwise. Here's a way that does what you need, still using the ternary operator:
#!/usr/bin/perl
use warnings;
use strict;
use List::Util qw (first);
my @a = qw( 1 2 3 4 5 );
my @b = qw( 9 8 2 1 5 );
my @z;
for my $elem ( @a ){
my $result = ( first { $elem == $_ } @b )
? '+'
: '-';
push @z, $result;
}
print @z;
Output:
++--+
Edit: Changed from using alpha to using numeric as to not confuse the OP with 'eq'.
Update: Please see this post by Anomalous Monk as to why my choice of using first() from List::Util should be replaced with any() from List::MoreUtils. It's a drop-in replacement in fact. | [reply] [d/l] [select] |
|
Thanks for that.If I should not really do it this way what would be a best and shortest method?
| [reply] |
|
for my $elem ( @a ){
push @z, $_ = ( first { $elem eq $_ } @b ) ? '+' : '-';
}
| [reply] [d/l] |
|
|
Re: Failed array attemp
by AnomalousMonk (Archbishop) on May 13, 2012 at 22:57 UTC
|
A number of replies use the grep built-in to scan an array for the presence of an element of another array. The problem with grep is that it will always scan the entire array even though the OPer only seems interested in the first occurrence of the element in the scanned array. List::MoreUtils::any will stop scanning at the first occurrence. For some value of the product of the sizes of the two arrays (100,000? 1,000,000? ...? Benchmark to find out), this difference in behavior will result in a significant performance win for any. For sufficiently small arrays, the difference is trivial.
| [reply] [d/l] [select] |
|
this difference in behavior will result in a significant performance win for any.
It'll only save a constant factor and so it'll still be O(XY) whereas using a temporary hash would be O(X).
The cost of using a hash would be additional storage on the order of O(Y) but, as you are already storing the array, this increase is just a constant factor.
Comparatively, stopping at the first found element is better the more duplication there is in the array. But the more duplication in the array, the less the storage cost of using a temporary hash instead (for the theoretically significant improvement.)
If you want efficiency, and aren't so short on storage that you can't do it, using a temporary hash is almost certainly the way to go.
-sauoq
"My two cents aren't worth a dime.";
| [reply] |
Re: Failed array attemp
by sauoq (Abbot) on May 13, 2012 at 23:07 UTC
|
for my $x (@x) { push @z, (grep {$_ == $x} @y) ? '+' : '-'; }
If your arrays stay short, this method might be fine, but you might want to avoid iterating over @y for each and every element of @z. A better way to do it would be:
my %k = map {($_, 1)} @y;
for my $x (@x) { push @z2, (exists $k{$x}) ? '+' : '-'; }
And if this is something you are doing a lot, you should just wrap it up in a sub so you can call it with references to two arrays and get a reference to a newly constructed array in return.
Don't worry about having short code. My second example is a little longer but still more efficient. You should focus on writing good and readable code and then reusing it properly.
-sauoq
"My two cents aren't worth a dime.";
| [reply] [d/l] [select] |
Re: Failed array attemp
by kcott (Archbishop) on May 14, 2012 at 00:05 UTC
|
any{$x == $_ } @y ? push(@z, "y") : push(@z, "n");
by writing:
push @z, any {$x == $_ } @y ? "y" : "n";
That's really just intended as an example. stevieb has provided an excellent response with a complete script above.
"I really want to use the ? : ; scheme properly ..."
Take a look at: Short form (ternary) if else. It discusses many aspects of ternary operator usage.
| [reply] [d/l] [select] |