How to assign a group of array elements a value in Perl? - arrays

I want to initialize a group of array elements the same value using a single line. I know I could use a for loop, but I want to know if there is a way simpler way to do it.
for e.g., I have an array of zeros. And I want to initialize elements 4 to 9 as 1. I would think of doing something like,
my #array = (0) x 10;
for my $i (3 .. 8) {
$array[$i] = 1;
}

Why not use an array slice?
#array = (0) x 10;
#array[3..8] = (1) x 6; # or something > 6
This is easier to understand than a splice and clearer than a map.
Instead of supplying a single index, we use a list [3..8]. We have to adjust the sigil to #, because we want a list context.

One approach:
my #array = (0) x 3, (1) x 6, 0;
Another approach:
my #array = map { $_ >= 3 && $_ <= 8 ? 1 : 0 } (0 .. 9);
Or, if you mean that you've already set #array to (0) x 10, and are just looking for a one-liner to set a range of values to 1:
splice #array, 3, 6, (1) x 6;

Related

How to dynamically create array and assign value in TCL

I want to create array dynamically and assign value to it. e.g.
set num 0
foreach i {1 2 3} {
set count$i(sp) {$num}
incr num
set count$i(ep) [expr $num+1]
}
When I ran your sample code, this error comes up:
can't read "i(sp)": variable isn't array
which is because the $i(sp) part of count$i(sp) looks like a reference to an existing array i.
You'll want to isolate $i from (sp) with curly brackets:
set num 0
foreach i {1 2 3} {
set count${i}(sp) {$num}
incr num
set count${i}(ep) [expr $num+1]
}
which creates three arrays:
tcl8.6.8> parray count1
count1(ep) = 2
count1(sp) = $num
tcl8.6.8> parray count2
count2(ep) = 3
count2(sp) = $num
tcl8.6.8> parray count3
count3(ep) = 4
count3(sp) = $num
Note above that curly brackets around $num use the literal string $num.
You'll want to remove those curly brackets:
set num 0
foreach i {1 2 3} {
set count${i}(sp) $num
incr num
set count${i}(ep) [expr $num+1]
}
tcl8.6.8> parray count1
count1(ep) = 2
count1(sp) = 0
tcl8.6.8> parray count2
count2(ep) = 3
count2(sp) = 1
tcl8.6.8> parray count3
count3(ep) = 4
count3(sp) = 2
However, please explain exactly what you mean by "dynamically creating a Tcl array". Do you really want three separate arrays or could you have one array with more names, like this:
set num 0
foreach i {1 2 3} {
set count($i,sp) $num
incr num
set count($i,ep) [expr $num+1]
}
tcl8.6.8> parray count
count(1,ep) = 2
count(1,sp) = 0
count(2,ep) = 3
count(2,sp) = 1
count(3,ep) = 4
count(3,sp) = 2
If you can avoid using a dynamic variable name like that, do so; they're often more trouble than they're worth. (You can use “composite” element names like $i,sp instead.) Otherwise — perhaps because of other code that accesses the array — the simplest way of handling them is to use upvar to make an alias to the variable with a fixed name. In particular, upvar 0 makes an alias to a variable in the same scope:
set num 0
foreach i {1 2 3} {
upvar 0 count$i ary
set ary(sp) {$num}
incr num
set ary(ep) [expr $num+1]
}
The only problem with upvar 0 is that you can't really undo it; that's why you almost always only use it within a procedure so the aliasing goes away naturally when the procedure exits.
Variable aliases are extremely efficient within the context of a procedure, substantially more so than using compound variable names in multiple places.
Also, {$num} looks suspicious. It's not wrong code in that it has a correct interpretation, but might not do what you expect and is often indicative of a problem, as $num isn't substituted. You might prefer [list $num].
You might enjoy using a dict instead of an array:
set count {}
set num 0
foreach i {1 2 3} {
dict set count $i sp $num
incr num
dict set count $i ep [expr {$num + 1}]
}
set count ;# -> 1 {sp 0 ep 2} 2 {sp 1 ep 3} 3 {sp 2 ep 4}
dict get $count 3 ep ;# -> 4

do I make this method work. The goal is to create a new set or array in which each element in the original set is multiplied by 2

def new_array(nums)
i = 0
double = []
while i <= nums.length
new_array = double[i] * 2
double << new_array
i += 1
end
return double
end
puts new_array[2, 4, 6, 8]
You can use map to create a new array with each value multiplied by 2.
def array_times_two(original_array)
original_array.map { |n| n * 2 }
end
array_times_two([2, 4, 6, 8])
=> [4, 8, 12, 16]
new_array = double[i] * 2
You are mixing up your variables. new_array is not an array, it is the name of your method. double is an empty array, so double[i] does not exist. Also, it loops once too many (the loop starts to count at zero). Try this:
while i < nums.length # not <=
double << nums[i] * 2
i += 1
end
Futhermore, call your method like this: puts new_array([2, 4, 6, 8]) , so with brackets (or put a space between the method and the argument), otherwise ruby itself will be mixed up.
Doubling the elements of an array is better done using map or map! (depending on what exactly you want to achieve), but I guess you try to implement the algorithm in this hard way (without using the Ruby core library) as a programming exercise, so I focus on your question why your approach does not work:
In your code, when you execute
new_array = double[i] * 2
double[i] is always nil, because you initialize double to the empty array and update it later, after the multiplication. You obviously can't multiply nil and will get an error message from this line.

How can I tie an array slice to the original array so all changes that are made to one are made to both?

I need to be able to tie an array slice to the original array in such a way that any changes made to the original array (including removing elements) will also be made to the array slice. Is there a way to do this?
The following example does not work how I want it to, but it is simply there to demonstrate the point I am trying to make.
Example:
my #array = 1 .. 10;
my #slice = #array[3 .. 8];
splice #array, 5, 2;
print "ARRAY: ";
print join ', ', #array;
print "\n";
print "SLICE: ";
print join ', ', #slice;
Output:
ARRAY: 1, 2, 3, 4, 5, 8, 9, 10
SLICE: 4, 5, 6, 7, 8, 9
What I am looking for is a way to tie the slice to the original array so the output would look like this instead:
ARRAY: 1, 2, 3, 4, 5, 8, 9, 10
SLICE: 4, 5, 8, 9
Removing 6 and 7 from the original array would also remove it from the array slice.
How can I achieve something like this?
As has been said, that's a tall order. The short answer is no. A slice creates a copy of the elements.
Perl does have a Tie feature that might be just the trick.
perltie - how to hide an object class in a simple variable
So it fundamentally changes what a variable is behind the scenes. A whole world of possibilities opens up, and your scenario just might be in there.
perltie on perldoc.perl.org
Updated post as requested using Object oriented method. Maintained original response after <========> Line
Here's the object oriented approach as mentioned in comments.
Sample.pm
package Sample;
use strict;
use warnings;
use Exporter qw(import);
use List::MoreUtils qw(first_index);
our #y = qw (3 4 5 6 7 8 9); # Add your method of acquiring #y value here
our #EXPORT = qw (SpliceV2 #y);
## Your Splice Version 2
sub SpliceV2(#) {
my ($position,$length,#x) = #_;
for (my $i=1;$i<=$length;$i++) {
my $removeditem = $x[$position];
my $remove = first_index { $_ eq $removeditem } #y;
splice #x, $position, 1;
splice #y, $remove, 1;
}
return #x;
}
1;
Main script:
#!/usr/bin/perl
use Sample;
my #x = qw(1 2 3 4 5 6 7 8 9 10);
#x = SpliceV2(4,2,#x);
print "X: #x\n";
print "Y: #y\n";
Original post below
<==========>
Assuming the items you are removing are unique like if you are basing it on primary keys of a database, then you can use first_index from List::MoreUtils;
Here's a sample.
use List::MoreUtils qw(first_index);
my #x = qw (1 2 3 4 5 6 7 8 9 10);
my #y = qw (4 5 6 7 8 9);
## Let's say you want to remove 2 items after the 5th index
my $position = 5;
my $length = 2;
## Get the value of items to remove first
for (my $i=1;$i<=$length;$i++) {
my $removeditem = $x[$position];
my $remove = first_index { $_ eq $removeditem } #y;
splice #x, $position, 1;
splice #y, $remove, 1;
}
print "Array X\n";
print "$_," foreach(#x);
print "\nArray Y\n";
print "$_," foreach(#y);
print "\n";
You should get the result you wanted.
Array X
1,2,3,4,5,8,9,10,
Array Y
4,5,8,9,
use strict;
use warnings;
use Data::Dumper;
my #array = 1..10;
my #slice = \#array[3..8];
splice #array, 5, 2;
print "ARRAY: ";
print join ', ', #array;
print "\n";
print "SLICE: ";
print join ', ', #slice;
Output:
ARRAY: 1, 2, 3, 4, 5, 8, 9, 10
SLICE: SCALAR(0x29dcef0), SCALAR(0x29dcf20), SCALAR(0x29dcf08), SCALAR(0x29dcfb0), SCALAR(0x29dcfc, SCALAR(0x29dd058)
Instead removing the items you could assign a zero value to the item (it will assign a zero value in the #slice too, because #slice holds a refference to #array) and then remove the 0's from you #slice and voila, you have your updated #slice with your deleted elements

Count Perl array size

I'm trying to print out the size of my array. I've followed a few other questions like this one on Stack Overflow. However, I never get the result I want.
All I wish for in this example is for the value of 3 to be printed as I have three indexes. All I get, from both print methods is 0.
my #arr;
$arr{1} = 1;
$arr{2} = 2;
$arr{3} = 3;
my $size = #arr;
print $size; # Prints 0
print scalar #arr; # Prints 0
What am I doing wrong, and how do I get the total size of an array when declared and populated this way?
First off:
my #arr;
$arr{1} = 1;
$arr{2} = 2;
$arr{3} = 3;
is nonsense. {} is for hash keys, so you are referring to %arr not #arr. use strict; and use warnings; would have told you this, and is just one tiny fragment of why they're considered mandatory.
To count the elements in an array, merely access it in a scalar context.
print scalar #arr;
if ( $num_elements < #arr ) { do_something(); }
But you would need to change your thing to
my #arr;
$arr[1] = 1;
$arr[2] = 2;
$arr[3] = 3;
And note - the first element of your array $arr[0] would be undefined.
$VAR1 = [
undef,
1,
2,
3
];
As a result, you would get a result of 4. To get the desired 'count of elements' you would need to filter the undefined items, with something like grep:
print scalar grep {defined} #arr;
This will take #arr filter it with grep (returning 3 elements) and then take the scalar value - count of elements, in this case 3.
But normally - you wouldn't do this. It's only necessary because you're trying to insert values into specific 'slots' in your array.
What you would do more commonly, is use either a direct assignment:
my #arr = ( 1, 2, 3 );
Or:
push ( #arr, 1 );
push ( #arr, 2 );
push ( #arr, 3 );
Which inserts the values at the end of the array. You would - if explicitly iterating - go from 0..$#arr but you rarely need to do this when you can do:
foreach my $element ( #arr ) {
print $element,"\n";
}
Or you can do it with a hash:
my %arr;
$arr{1} = 1;
$arr{2} = 2;
$arr{3} = 3;
This turns your array into a set of (unordered) key-value pairs, which you can access with keys %arr and do exactly the same:
print scalar keys %arr;
if ( $elements < keys %arr ) { do_something(); }
In this latter case, your hash will be:
$VAR1 = {
'1' => 1,
'3' => 3,
'2' => 2
};
I would suggest this is bad practice - if you have ordered values, the tool for the job is the array. If you have 'key' values, a hash is probably the tool for the job still - such as a 'request ID' or similar. You can typically tell the difference by looking at how you access the data, and whether there are any gaps (including from zero).
So to answer your question as asked:
my $size = #arr;
print $size; # prints 0
print scalar #arr; # prints 0
These don't work, because you never insert any values into #arr. But you do have a hash called %arr which you created implicitly. (And again - use strict; and use warnings; would have told you this).
You are initializing a hash, not an array.
To get the "size" of your hash you can write.
my $size = keys %arr;
I just thought there should be an illustration of your code run with USUW (use strict/use warnings) and what it adds to the troubleshooting process:
use strict;
use warnings;
my #arr;
...
And when you run it:
Global symbol "%arr" requires explicit package name (did you forget to declare "my %arr"?) at - line 9.
Global symbol "%arr" requires explicit package name (did you forget to declare "my %arr"?) at - line 10.
Global symbol "%arr" requires explicit package name (did you forget to declare "my %arr"?) at - line 11.
Execution of - aborted due to compilation errors.
So USUW.
You may be thinking that you are instantiating an element of #arr when you are typing in the following code:
$arr{1} = 1;
However, you are instantiating a hash doing that. This tells me that you are not using strict or you would have an error. Instead, change to brackets, like this:
$arr[1] = 1;

perl - operations allowed on array while iterating through it

What are the operations allowed on array, while iterating through it?
Is it possible to shift/unshift, pop/push, delete elements without confusing the iterator?
Is that any different for adding/removing key-value pair from hash?
Thank you for your help.
You can assign to existing elements, but should not add or remove them. So no shift, unshift, pop, push, or splice. perlsyn:
If any part of LIST is an array, foreach will get very confused if you add or remove elements within the loop body, for example with splice. So don't do that.
If you are iterating over a hash with each, you should also avoid adding or removing elements, except that you are explicitly allowed to remove the current element. each:
If you add or delete a hash's elements while iterating over it, the effect on the iterator is unspecified; for example, entries may be skipped or duplicated--so don't do that. Exception: It is always safe to delete the item most recently returned by each(), so the following code works properly:
But as it says, the worst that could happen is entries being skipped or duplicated; modifying an array you are looping over, on the other hand, can lead to segfaults.
As ysth has already pointed out, it is unwise to attempt to modify an array while iterating directly on its elements.
However, if one does want to modify an array dependent on the element values, the trick is to do it in reverse index order.
For example, say I have an array of numbers. I would like modifier the array so that every multiple of 4 has a string inserted after it, and every multiple of 5 is removed. I would accomplish that using the following:
use strict;
use warnings;
my #array = ( 1 .. 20 );
for my $i ( reverse 0 .. $#array ) {
# Insert after multiples of 4
if ( ( $array[$i] % 4 ) == 0 ) {
splice #array, $i + 1, 0, "insert";
}
# Remove multiples of 5
if ( ( $array[$i] % 5 ) == 0 ) {
splice #array, $i, 1;
}
}
use Data::Dump;
dd #array;
Outputs:
(
1 .. 4,
"insert",
6,
7,
8,
"insert",
9,
11,
12,
"insert",
13,
14,
16,
"insert",
17,
18,
19,
"insert",
)
Alternatively, if you want to transform an array, it's also possible to use map like so:
my #newarray = map {
( ( ($_) x !!( $_ % 5 ) ), # Remove multiples of 5
( ('insert') x !( $_ % 4 ) ), # Insert After multiples of 4
)
} ( 1 .. 20 );
use Data::Dump;
dd #newarray;

Resources