Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:
Hello
I have two arrays that contain a binary index of 12 elements (values of each element can be only 1 or 0). I want to combine the the indexes in element to element fashion as OR operator so that I have:
@array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1);
@array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1);
#result should be
@arrayResult = (0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1);
The only solution I came out with is a quite long script comparing element at position X in @array1 with the element at the same position in @array2, if both are 0 then I set the value in @arrayResult to 0 otherwise to 1. I do it in a nested loop with checking variables and so, but the whole seems to me to cumbersome for such a simple operation, and probably there is a straight operation in Perl to do this that I do not know. Or no other way the a nested loop?
Re: merging two arrays with OR operation
by pryrt (Abbot) on Mar 05, 2019 at 21:12 UTC
|
use bitwise OR ( | ) or logical OR ( || ) on each element. It doesn't have to be a long program, if you use map or other such perl idioms
# [id://1230926]
use warnings;
use strict;
my @array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1);
my @array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1);
my @arrayResult = map { $array1[$_] | $array2[$_] } 0 .. $#array1;
print "arrayResult = (@arrayResult)\n";
use Test::More tests => 1;
is_deeply( \@arrayResult , [0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1] )
| [reply] [d/l] [select] |
|
use Data::Dumper;
my @array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1);
my @array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1);
my @result;
if (@array1 != @array2) {
$equals = 0;
} else {
$equals = 1;
foreach (my $i = 0; $i < @array1; $i++) {
if (($array1[$i] eq 0)&&($array2[$i] eq 0)) {
push (@result, 0);
}
else{
push (@result, 1);
}
}
}
print Dumper \@result;
| [reply] [d/l] |
|
$result[$i] = $array1[$i] | $array2[$i];
and your for loop is better written:
for my $i (0 .. $#array1)
Note that for and foreach are identical aside from the spelling so personally I never use foreach just because that way I type less.
Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
| [reply] [d/l] [select] |
|
| [reply] [d/l] |
|
I think having a conversion from an array of bits into your bitwise integer (and vice versa) would be a good idea.
I'm not sure that bit_cmp(), which sounds like "bit compare" in my mental reading, quite fits with the OR or AND operations. It would make sense to me for the XOR (which is bits-not-equal) and XNOR (which is bits-are-equal, the same as ~($a ^ $b)), since those do a bitwise "compare". I guess the argument could be made that AND and OR are bitwise compares as well, but I don't think of them that way.) Since you're working with integers internally anyway, it seems easy enough just to use | & ^ ~(^) on the vars, rather than having a wrapper function to do the same thing. Then again, it wouldn't hurt...
As you can tell, I'm ambivalent whether bit_cmp() should be added. But I definitely vote to implement a conversion to/from a bit-array.
| [reply] [d/l] [select] |
Re: merging two arrays with OR operation
by choroba (Cardinal) on Mar 05, 2019 at 21:54 UTC
|
You can also use pack and unpack to convert the array of 0s and 1s to a binary number and operate on it directly:
my $template = 'B' . @array1;
my $v1 = pack $template, join "", @array1;
my $v2 = pack $template, join "", @array2;
my @array_result = split //, unpack $template, $v1 | $v2;
map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
| [reply] [d/l] [select] |
|
c:\@Work\Perl\monks>perl -le
"use warnings;
use strict;
;;
use Test::More 'no_plan';
use Test::NoWarnings;
;;
my @TESTS = (
'degenerate case',
[ [], [], [], ],
'simple cases',
[ [ 0 ], [ 0 ], [ 0 ], ],
[ [ 0 ], [ 1 ], [ 1 ], ],
[ [ 1 ], [ 0 ], [ 1 ], ],
[ [ 1 ], [ 1 ], [ 1 ], ],
'complex cases',
[ [ 0, 1, 0, 1, ],
[ 0, 0, 1, 1, ],
[ 0, 1, 1, 1, ],
],
[ [ 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, ],
[ 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, ],
[ 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, ],
],
);
;;
VECTOR:
for my $ar_vector (@TESTS) {
if (not ref $ar_vector) {
note $ar_vector;
next VECTOR;
}
;;
my ($ar_x, $ar_y, $ar_expected) = @$ar_vector;
;;
use constant TEMPLATE => '(B)*';
my $v1 = pack TEMPLATE, @$ar_x;
my $v2 = pack TEMPLATE, @$ar_y;
my @got = unpack TEMPLATE, $v1 | $v2;
is_deeply \@got, $ar_expected, qq{(@got)};
}
;;
done_testing;
;;
exit;
"
# degenerate case
ok 1 - ()
# simple cases
ok 2 - (0)
ok 3 - (1)
ok 4 - (1)
ok 5 - (1)
# complex cases
ok 6 - (0 1 1 1)
ok 7 - (0 1 1 1 0 1 1 1 1 1 1 1)
1..7
ok 8 - no warnings
1..8
Anonymous Monk: See also How to ask better questions using Test::More and sample data.
Update: Oops... Originally posted with template '(a)*' (which also works, but produces longer intermediate strings) instead of '(B)*': Fixed.
Give a man a fish: <%-{-{-{-<
| [reply] [d/l] [select] |
Re: merging two arrays with OR operation
by BillKSmith (Monsignor) on Mar 05, 2019 at 21:49 UTC
|
I prefer to use a module where the function name documents the operation.
# [id://1230926]
use warnings;
use strict;
use List::MoreUtils qw(pairwise);
my @array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1);
my @array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1);
my @arrayResult = pairwise {$a or $b} @array1, @array2;
print "arrayResult = (@arrayResult)\n";
use Test::More tests => 1;
is_deeply( \@arrayResult , [0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1] )
Thanks pryrt to the test script.
| [reply] [d/l] |
Re: merging two arrays with OR operation
by roboticus (Chancellor) on Mar 05, 2019 at 21:24 UTC
|
Anonymous Monk:
Performing an operation pairwise on a pair of lists is a common enough task that there's a function in the List::MoreUtils package to help you with it: pairwise().
So for your task, you'd simply do it like:
$ cat pm_1230926.pl
use strict;
use warnings;
use List::MoreUtils 'pairwise';
my @array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1);
my @array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1);
my @arrayResult = pairwise { $a<$b ? $b : $a } @array1, @array2;
print "Result: ", join(", ", @arrayResult), "\n";
$ perl pm_1230926.pl
Result: 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1
Essentially, you give it a block of code { $a<$b ? $b : $a } and a pair of lists to operate on. The values $a and $b will be the next item in the first and second list, respectively. The result will be the list of values obtained by executing the code block on each pair of items in the list. So in our code block, we check to see if $a is less than $b. If so, we'll return $b otherwise we'll return $a.
Update: choroba suggested to me an even better way:
my @arrayResult = pairwise { $a | $b } @array1, @array2;
...roboticus
When your only tool is a hammer, all problems look like your thumb. | [reply] [d/l] [select] |
Re: merging two arrays with OR operation
by tybalt89 (Monsignor) on Mar 06, 2019 at 02:05 UTC
|
#!/usr/bin/perl
# https://perlmonks.org/?node_id=1230926
use strict;
use warnings;
my @array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1);
my @array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1);
#result should be
#@arrayResult = (0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1);
my @arrayResult = split ' ', "@array1" | "@array2";
use Data::Dump 'dd'; dd @arrayResult;
| [reply] [d/l] |
Re: merging two arrays with OR operation
by rsFalse (Chaplain) on Mar 05, 2019 at 22:54 UTC
|
Sometimes you may compose a binary representation of number (e.g. '0b010101111111') (docs: perlnumber), eval it, use bitwise op, and return to binary representation using sprintf "%b", with '0${len}' for leading zeroes.
#!/usr/bin/perl -l
use strict;
use warnings;
my @array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1);
my @array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1);
my $bits1 = eval '0b' . join '', @array1;
my $bits2 = eval '0b' . join '', @array2;
my $bitsResult = $bits1 | $bits2;
my $len = scalar @array1;
my @arrayResult = split //, sprintf "%0${len}b", $bitsResult;
print "\$bits1:[$bits1], \$bits2:[$bits2], \$bitsResult:[$bitsResult]"
+;
print "Result: ", join(", ", @arrayResult), "\n";
OUTPUT:
$bits1:[1407], $bits2:[855], $bitsResult:[1919]
Result: 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1
| [reply] [d/l] [select] |
|
#!/usr/bin/perl -wl
use strict;
my @array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1);
my @array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1);
my $bits1 = join '', @array1;
my $bits2 = join '', @array2;
print $bits1 | $bits2;
use feature 'bitwise'; # from v5.22
print $bits1 | $bits2;
print $bits1 |. $bits2;
# So...
print "Result: ", join ', ', split //, $bits1 |. $bits2;
OUTPUT:
011101111111
4294967295
011101111111
Result: 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1
| [reply] [d/l] [select] |
Re: merging two arrays with OR operation
by Random_Walk (Prior) on Mar 06, 2019 at 11:10 UTC
|
Here is a fun little one with map :) Just for fun, anything above is a 'better' way to do it
use strict;
use warnings;
my @array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1);
my @array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1);
print "@array1\n";
print "@array2\n";
my $i;
my @result= map {$_ += $array2[$i++];int($_/2 +.6)} @array1;
print "@result\n";
Update
WARNING Lookout though, it messes with the content of array 1
# Here is a version that will not break array 1
use strict;
use warnings;
my @array1 = (0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1);
my @array2 = (0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1);
print "@array1\n";
print "@array2\n";
my $i;
my @result= map {int( ($_+$array2[$i++])/2 +.6)} @array1;
print "@result\n";
Cheers, R.
Pereant, qui ante nos nostra dixerunt!
| [reply] [d/l] [select] |
|
Same, but with another one variant inside the map, using shift-right op (Shift Operators):
my @result = map { $_ + $array2[ $i++ ] + 1 >> 1 } @array1;
| [reply] [d/l] |
Re: merging two arrays with OR operation
by johngg (Canon) on Mar 06, 2019 at 23:31 UTC
|
Late to the party again but here's a solution that uses vec to construct bitstrings from the arrays that can be binary ORed to produce a result. The vector and the length of the original array are held in a hashref so that problems with reconstructing the array when the vector extends to a byte boundary can be avoided.
use 5.026;
use warnings;
my @array1 = ( 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1 );
my @array2 = ( 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1 );
my @arrayResult = vec2array(
vecORvec( array2vec( @array1 ), array2vec( @array2 ) )
);
say join q{, }, @arrayResult;
sub array2vec
{
my $rhVec = {};
$rhVec->{ len } = scalar @_;
$rhVec->{ vec } = q{};
vec( $rhVec->{ vec }, $_, 1 ) = $_[ $_ ]
for 0 .. $rhVec->{ len } - 1;
return $rhVec;
}
sub vec2array
{
my @array = map {
vec $_[ 0 ]->{ vec }, $_, 1
} 0 .. $_[ 0 ]->{ len } - 1;
return @array;
}
sub vecORvec
{
my( $rhVec1, $rhVec2 ) = @_;
my $rhNewVec;
$rhNewVec->{ len } = $rhVec1->{ len };
$rhNewVec->{ vec } = $rhVec1->{ vec } | $rhVec2->{ vec };
return $rhNewVec;
}
The result.
0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1
I hope this is of interest.
Update: Added some sanity checking and confined the conversion of array to vector to the rule of false/true corresponding to bit values of 0 or 1 respectively. I also added AND and XOR routines, which along with OR will cope with vectors of differing length, and borrowed and adapted AnomalousMonk's test data.
The test results.
Possibly a waste of time but feeping creaturism can be fun :-)
Update 2: Took repetitive code out of the three comparison routines, (vecANDvec(), vecORvec(), vecXORvec()), and moved it into vecANYvec() which is passed a coderef telling it which operation to do. All tests still pass.
| [reply] [d/l] [select] |
Re: merging two arrays with OR operation
by karlgoethebier (Abbot) on Mar 06, 2019 at 14:04 UTC
|
What else should I say: This thread made my day. Very nice. Regards, Karl
«The Crux of the Biscuit is the Apostrophe»
perl -MCrypt::CBC -E 'say Crypt::CBC->new(-key=>'kgb',-cipher=>"Blowfish")->decrypt_hex($ENV{KARL});'Help
| [reply] [d/l] |
|
|