Re: Inverse slices
by tybalt89 (Monsignor) on Nov 01, 2024 at 03:19 UTC
|
#!/usr/bin/perl
use strict; # https://perlmonks.org/?node_id=11162546
use warnings;
$SIG{__WARN__} = sub { die @_ };
# Is there a better way to get an inverse slice than to do something l
+ike:
my @array = ('aa'..'zz');
my @slice_idx = (6,13,42,66,69);
# Slice
my @slice = @array[@slice_idx];
use Data::Dump 'dd'; dd { 'array' => \@array,
'slice_idx' => \@slice_idx, 'slice' => \@slice };
my %slice_idx = map { $_ => 1 } @slice_idx;
my @invslice_idx = grep { ! exists $slice_idx{$_} } 0 .. $#array;
# Inverse slice.
my @invslice = @array[@invslice_idx];
use Data::Dump 'dd'; dd { 'idx' => \@invslice_idx, 'invslice' => \@inv
+slice };
my @newinvslice = @array;
@newinvslice[@slice_idx] = ();
@newinvslice = grep defined, @newinvslice;
use Data::Dump 'dd'; dd { 'newinvslice' => \@newinvslice };
use List::Util qw( none );
my @new2invslice = @array[
grep { my $idx = $_; none { $idx == $_ } @slice_idx } 0 .. $#array ]
+;
use Data::Dump 'dd'; dd { 'new2invslice' => \@new2invslice };
my @new3invslice = @array;
splice @new3invslice, $_, 1 for reverse @slice_idx;
use Data::Dump 'dd'; dd { 'new3invslice' => \@new3invslice };
use List::Util qw( uniq );
my @new4invslice =
@array[ (uniq @slice_idx, 0 .. $#array)[@slice_idx .. $#array ] ];
use Data::Dump 'dd'; dd { 'new4invslice' => \@new4invslice };
Outputs:
{
array => ["aa" .. "zz"],
slice => ["ag", "an", "bq", "co", "cr"],
slice_idx => [6, 13, 42, 66, 69],
}
{
idx => [0 .. 5, 7 .. 12, 14 .. 41, 43 .. 65, 67, 68, 70 .. 675],
invslice => [
"aa" .. "af",
"ah" .. "am",
"ao" .. "bp",
"br" .. "cn",
"cp",
"cq",
"cs" .. "zz",
],
}
{
newinvslice => [
"aa" .. "af",
"ah" .. "am",
"ao" .. "bp",
"br" .. "cn",
"cp",
"cq",
"cs" .. "zz",
],
}
{
new2invslice => [
"aa" .. "af",
"ah" .. "am",
"ao" .. "bp",
"br" .. "cn",
"cp",
"cq",
"cs" .. "zz",
],
}
{
new3invslice => [
"aa" .. "af",
"ah" .. "am",
"ao" .. "bp",
"br" .. "cn",
"cp",
"cq",
"cs" .. "zz",
],
}
{
new4invslice => [
"aa" .. "af",
"ah" .. "am",
"ao" .. "bp",
"br" .. "cn",
"cp",
"cq",
"cs" .. "zz",
],
}
| [reply] [d/l] [select] |
|
|
use List::AllUtils qw( singleton );
my @new6invslice = @array[ singleton 0 .. $#array, @slice_idx ];
If that's not better, we need a better definition of 'better'. (WARNING: possible infinite recursion encountered!)
| [reply] [d/l] |
|
|
Some suggestions, while I wait for Ubuntu to upgrade...
My comments in all CAPS.
Most importantly - regarding @newinvslice - Ikegami was right to point out that an undef value is not a sufficiently safe criteria (AKA the semi-predicate problem)
I have a solution using the ref of a scalar var which MUST be unique.
A bit clumsy, because Perl has no === operator, but I am open for more elegant suggestions. :)
#!/usr/bin/perl
use strict; # https://perlmonks.org/?node_id=11162546
use warnings;
use Data::Dump 'dd';
use Test::More;
$SIG{__WARN__} = sub { die @_ };
# Is there a better way to get an inverse slice than to do something l
+ike:
my @array = ('aa'..'zz');
my @slice_idx = (6,13,42,66,69);
# Slice
my @slice = @array[@slice_idx];
my $expected = [
"aa" .. "af",
"ah" .. "am",
"ao" .. "bp",
"br" .. "cn",
"cp",
"cq",
"cs" .. "zz",
];
dd {
'array' => \@array,
'slice_idx' => \@slice_idx,
'slice' => \@slice,
'expected' => $expected,
};
{
my $TEST = "not in idx hash";
my %slice_idx;
@slice_idx{@slice_idx} = (); # NO MAP NEEDED
my @invslice_idx = grep { ! exists $slice_idx{$_} } 0 .. $#array;
# Inverse slice.
my @invslice = @array[@invslice_idx];
is_deeply(\@invslice, $expected, $TEST ) or
diag dd { 'idx' => \@invslice_idx, 'invslice' => \@invslice };
}
{
my $TEST = "not delete value";
my @newinvslice = @array;
my $delete;
@newinvslice[@slice_idx] = (\$delete) x @slice_idx; # UNIQUE REF V
+ALUE
@newinvslice = grep { ref $_ ne "SCALAR" or $_ ne \$delete } @ne
+winvslice;
is_deeply(\@newinvslice ,$expected, $TEST) or
diag dd { 'newinvslice' => \@newinvslice };
}
{
my $TEST = "splice backwards";
my @new3invslice = @array;
splice @new3invslice, $_, 1 for sort { $a < $b } @slice_idx; # ENS
+URE IDX SORTED
is_deeply(\@new3invslice, $expected, $TEST) or
diag dd { 'new3invslice' => \@new3invslice };
}
done_testing;
{
array => ["aa" .. "zz"],
expected => [
"aa" .. "af",
"ah" .. "am",
"ao" .. "bp",
"br" .. "cn",
"cp",
"cq",
"cs" .. "zz",
],
slice => ["ag", "an", "bq", "co", "cr"],
slice_idx => [6, 13, 42, 66, 69],
}
ok 1 - not in idx hash
ok 2 - not delete value
ok 3 - splice backwards
1..3
Updates
Bugfix s/and/or/ | [reply] [d/l] [select] |
|
|
#!/usr/bin/perl
use strict; # https://perlmonks.org/?node_id=11162546
use warnings;
$SIG{__WARN__} = sub { die @_ };
# Is there a better way to get an inverse slice than to do something l
+ike:
my @array = ('aa'..'zz');
my @slice_idx = (6,13,42,66,69);
# Slice
my @slice = @array[@slice_idx];
use Data::Dump 'dd'; dd { 'array' => \@array,
'slice_idx' => \@slice_idx, 'slice' => \@slice };
my %slice_idx = map { $_ => 1 } @slice_idx;
my @invslice_idx = grep { ! exists $slice_idx{$_} } 0 .. $#array;
# Inverse slice.
my @invslice = @array[@invslice_idx];
use Data::Dump 'dd'; dd { 'idx' => \@invslice_idx, 'invslice' => \@inv
+slice };
my @wanted = 0 .. $#array;
@wanted[@slice_idx] = ();
my @new6invslice = @array[ grep defined, @wanted ];
use Data::Dump 'dd'; dd { 'new6invslice' => \@new6invslice };
Outputs:
{
array => ["aa" .. "zz"],
slice => ["ag", "an", "bq", "co", "cr"],
slice_idx => [6, 13, 42, 66, 69],
}
{
idx => [0 .. 5, 7 .. 12, 14 .. 41, 43 .. 65, 67, 68, 70 .. 675],
invslice => [
"aa" .. "af",
"ah" .. "am",
"ao" .. "bp",
"br" .. "cn",
"cp",
"cq",
"cs" .. "zz",
],
}
{
new6invslice => [
"aa" .. "af",
"ah" .. "am",
"ao" .. "bp",
"br" .. "cn",
"cp",
"cq",
"cs" .. "zz",
],
}
| [reply] [d/l] [select] |
Re: Inverse slices
by ysth (Canon) on Nov 01, 2024 at 01:04 UTC
|
That works. You could make it more terse, if you wanted:
my @array = ('aa'..'zz');
my %slice_idx = map( ($_ => 1), 6,13,42,66,69 );
my @invslice = @array[ grep ! exists $slice_idx{$_}, 0..$#array ];
Or use Array::Set, or Set::Array, or Set::Scalar.
--
A math joke: r = | |csc(θ)|+|sec(θ)| |-| |csc(θ)|-|sec(θ)| |
| [reply] [d/l] |
|
|
my %slice_idx;
@slice_idx{6, 13, 42, 66, 69} = ();
map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
| [reply] [d/l] [select] |
Re: Inverse slices
by ikegami (Patriarch) on Nov 01, 2024 at 00:59 UTC
|
Better how?
Shorter? Put the code in a sub.
More readable? Put the code in a sub.
Faster? Sort the indexes and use a merge sort-like approach written in C to filter out the unwanted elements.
Alternative approach if the array only has defined elements:
my @invslice = @array;
@invslice[ @slice_idx ] = ();
@invslice = grep defined, @invslice;
Update: Replaced undef @invslice[ @slice_idx ]; (which doesn't work) with @invslice[ @slice_idx ] = ();.
| [reply] [d/l] [select] |
|
|
While this use of undef works in all perl 5 versions I am aware of, it is in fact not documented. I prefer
@invslice[ @slice_idx ] = ();
--
A math joke: r = | |csc(θ)|+|sec(θ)| |-| |csc(θ)|-|sec(θ)| |
| [reply] [d/l] |
|
|
While this use of undef works in all perl 5 versions I am aware of, it is in fact not documented
Really? It didn't work on any perl version I tried. For me, ikegami's slice undef trick only undefined the last element in the slice.
| [reply] |
|
|
Re: Inverse slices
by johngg (Canon) on Nov 01, 2024 at 11:59 UTC
|
johngg@aleatico:~$ perl -Mstrict -Mwarnings -MData::Dumper -E 'say q{}
+;
my @arr = ( q{a} .. q{m} );
my %idx = map { $_ => 1 } 2, 7 .. 9, 12;
my @slc;
my @inv;
push @{ $idx{ $_ } ? \ @slc : \ @inv }, $arr[ $_ ]
for 0 .. $#arr;
print Data::Dumper->Dumpxs(
[ \ @arr, \ @slc, \ @inv ],
[ qw{ *arr *slc *inv } ] );'
@arr = (
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm'
);
@slc = (
'c',
'h',
'i',
'j',
'm'
);
@inv = (
'a',
'b',
'd',
'e',
'f',
'g',
'k',
'l'
);
I hope this is of interest.
Update: Removed erroneous space in Dumpxs call which was mucking up the output.
| [reply] [d/l] [select] |
Re: Inverse slices
by oodler (Beadle) on Nov 01, 2024 at 17:21 UTC
|
splice with map could work, but the better approach if possible would be to catch it on the front end, and filter it; you may make optimizations once you get it working for your purposes. A possible improvement would be to replace the grep with List::Util's any function.
use strict;
use warnings;
use Data::Dumper;
my @skip_idx = (6,13,42,66,69);
my @array = ();
my @invarray = ();
my $idx = -1;
foreach my $str ('aa'..'zz') {
++$idx;
if (grep { /^$idx$/ } @skip_idx) { # could use List::Util::any here
push @invarray, $str;
print "skipped '$str' (index of $idx)\n";
}
push @array, $str;
}
print Data::Dumper::Dumper(\@invarray);
Output,
$ perl test.pl
skipped 'ag' (index of 6)
skipped 'an' (index of 13)
skipped 'bq' (index of 42)
skipped 'co' (index of 66)
skipped 'cr' (index of 69)
$VAR1 = [
'ag',
'an',
'bq',
'co',
'cr'
];
| [reply] [d/l] [select] |