How do I shorten an array in Perl 6? - arrays

How could I cut off an array or an array-reference in Perl 6?
In Perl 5, I can do this:
my $d = [0 .. 9];
$#$d = 4;
In Perl 6, I get an error if I try this:
my $d = [0 .. 9];
$d.end = 4; # Cannot modify an immutable Int
This works, but it looks less beautiful than the Perl 5 way and may be expensive:
$d.=splice(0, 5);

There is a simple way:
my $d = [0..9];
$d[5..*] :delete;
That is problematic if the array is an infinite one.
$d.splice(5) also has the same problem.
Your best bet is likely to be $d = [ $d[^5] ] in the average case where you may not know anything about the array, and need a mutable Array.
If you don't need it to be mutable $d = $d[^5] which returns a List may be better.

splice is probably the best choice here, but you can also shorten to five elements using the ^N range constructor shortcut (I call this the "up until" "operator" but I am sure there is a more correct name since it is a constructor of a Range):
> my $d = [ 0 .. 9 ];
> $d.elems
> 10
> $d = [ $d[^5] ]
[0 1 2 3 4]
> $d.elems
5
> $d
[0 1 2 3 4]
"The caret is ... a prefix operator for constructing numeric ranges
starting from zero".
(From the Range documentation)
One can argue that perl6 is "perl-ish" in the sense it usually has an explicit version of some operation (using a kind of "predictable" syntax - a method, a routine, and :adverb, etc.) that is understandable if you are not familiar with the language, and then a shortcut-ish variant.
I'm not sure which approach (splice vs. the shortcut vs. using :delete as Brad Gilbert mentions) would have an advantage in speed or memory use. If you run:
perl6 --profile -e 'my $d = [ 0 .. 9 ]; $d=[ $d[^5] ]'
perl6 --profile -e 'my $d = [ 0 .. 9 ]; $d.=splice(0, 5);'
you can see a slight difference. The difference might be more significant if you compared with a real program and workload.

Another option is using the xx operator:
my $d = [0..9];
$d.pop xx 4; #-> (9 8 7 6)
say $d; #-> [0 1 2 3 4 5]
$d = [0..9];
$d.shift xx 5 #-> (0 1 2 3 4)
say $d; #-> [5 6 7 8 9)

Related

delete elements in array bash

im trying to delete two elements at same time in my array in bash script
my code is
elegidos = (1 2 3 4 5 6 7)
i=0
j=${#elegidos[#]}
delete=($i $j)
while [ $i -le $j ]; do
#elegidosmenosdos=${elegidos[#]/$i:$j}
echo ${elegidos[#]/$delete}
delete=($i $j)
let "i++"
let "j--"
done
the output that i have is
1 2 3 4 5 6 7
1 2 3 4 5 6 7
2 3 4 5 6 7
1 3 4 5 6 7
and i need 21 different combinations with five elements using seven numbers
output example
1 2 3 4 5
2 3 4 5 6
7 6 5 4 3
.
.
.
.
.(21 MORE)
Well, it took a minute to suss out that you were simply wanting to do an end-to-middle delete of two-elements at a time working from the ends of the array, deleting the end nodes at the same time, increment/decrement your counters and repeat until you reached the middle. Why you want to do it this way is a bit of a mystery. You can, of course, simply unset elegidos and unset all values at once. However, if you want to work in from both ends -- that fine too if you have a purpose for it.
You have several problems in your script. In bash, all arrays are zero indexed. Your array has 7-elements, so the valid indexes are 0-6. Therefore, you j was wrong to begin with. You needed to subtract 1 from the number of elements to get the index for the end-element, e.g.
i=0
j=$((${#elegidos[#]} - 1))
bash provides a C-style loop that can greatly simplify your task. While you are free to use a while a C-style loop can handle the index increment and decrement seamlessly, e.g.
for ((; i <= j; i++, j--)); do
note the i <= j. If you have an odd-number of elements in the array, on your last iteration, you will simply be deleting one-value instead of two. To handle that condition you need a simple test within the loop to check whether [ "$i" = "$j" ] (or using the arithmetic comparison (( i == j ))).
Putting that altogether, you could refine your element removal to empty the array two-elements at a time to something similar to the following:
#!/bin/bash
elegidos=(1 2 3 4 5 6 7)
i=0
j=$((${#elegidos[#]} - 1))
delete=($i $j)
for ((; i <= j; i++, j--)); do
declare -p elegidos
unset elegidos[$i]
[ "$i" != "$j" ] && unset elegidos[$j]
delete=($i $j)
done
Example Use/Output
$ bash array_del.sh
declare -a elegidos='([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7")'
declare -a elegidos='([1]="2" [2]="3" [3]="4" [4]="5" [5]="6")'
declare -a elegidos='([2]="3" [3]="4" [4]="5")'
declare -a elegidos='([3]="4")'
You can see above, that on the first 3-iterations, both end-elements are removed. However, notice on the last removal (there originally being and odd number of elements, only the single-last value is removed on the final iteration.
Look things over and let me know if I captured what you were attempting and whether you have any further questions. If your intent was something else, drop a comment and I'm happy to help further.

How to identify the position of a sequence of elements in an array in bash

I'm learning bash and I'm trying identify a sequence elements (patch or subarray) position into a array.
For example:
array=(9 5 8 3 2 7 5 9 0 1 1 5 4 3 8 9 6 2 6 5 7 9 8);
patch=(0 1 1 5)
I would like to obtain a output equals to 8 (start position of my patch in relation to array) or 11 (final position).
bash doesn't really have any built-in facility to do this; you need to walk the array yourself:
for ((i=0; i<${#array[#]}; i++)); do
for ((j=0; j<${#patch[#]}; j++)); do
# Make sure the corresponding elements
# match, or give up. RHS is quoted to ensure
# actual string equality, rather than just pattern matching
[[ ${array[i+j] == "${patch[j]}" ]] || break
done
if [[ $j == ${#patch[#] ]]; then
# All the comparisons succeeded!
start=$i
finish=$((i+j-1))
break
fi
done

bad substitution shell- trying to use variable as name of array

#!/bin/bash
array=( 2 4 5 8 15 )
a_2=( 2 4 8 10 )
a_4=( 2 4 8 10 )
a_5=( 10 12 )
a_8=( 8 12 )
a_15=( 2 4 )
numberOfTests=5
while [ $i -lt ${#array[#]} ]; do
j=0
currentArray =${array[$i]}
*while [ $j -lt ${#a_$currentArray [#]} ]; do #### this line i get ->>>> bad substitution*
./test1.sh "${array[$i]}" -c "${a_"$currentArray "[$j]}" &
let j=j+1
done
let i=i+1
done
so Im trying this code, loop over an array(called array), The array should point out
the array number we are now looping(a_X). And every time to point out the current place and value.
can anybody help me how im using the $currentArray to work properly so I can know the length of the array and the value?
I get in the line I marked an error.
Thank you guys!
The simplest solution is to store the full names of the arrays, not just the numerical suffix, in array. Then you can use indirect parameter expansion while iterating directly over the values, not the indices, of the arrays.
# Omitting numberOfTests has it does not seem to be used
array=(a_2 a_4 a_5 a_8 a_15)
a_2=( 2 4 8 10 )
a_4=( 2 4 8 10 )
a_5=( 10 12 )
a_8=( 8 12 )
a_15=( 2 4 )
for arr in "${array[#]}"; do
currentArray=$arr[#]
for value in "${!currentArray}"; do
./test1.h "${arr#a_}" -c "$value" &
done
done

How to Create a list with Missing Numbers in a non continious list of numbers using TCL

I wanted to create a list of numbers with missing numbers in a given list as provided in the example below
Existing list { 1,3, 5, 9 , 13, 15}
Resultant list { 2,4,6,7,8,10,11,12,14}
Extended TCL has the function intersect3 which as one of its return values gives a list of A-B. You could intersect your list with a list of all possible numbers that span your list.
If you don't use Extended TCL, you'll have to implement something yourself.
I hardly ever use TCL, so maybe there's a better way, but the basic approach is to just sort the list, then run through it and find the missing values:
#!/usr/bin/tclsh
set A {1 3 5 9 13 15}
set A [lsort -integer $A]
set B {}
set x 0
set y [lindex $A $x]
while {$x < [llength $A]} {
set i [lindex $A $x]
while {$y < $i} {
lappend B $y
incr y
}
incr x
incr y
}
puts $B
Output:
2 4 6 7 8 10 11 12 14
paddy's answer looks pretty good. This is a bit shorter, assumes the list is already sorted.
package require Tcl 8.5
set A {1 3 5 9 13 15}
set result [list]
for {set i [lindex $A 0]; incr i} {$i < [lindex $A end]} {incr i} {
if {$i ni $A} {
lappend result $i
}
}
See http://tcl.tk/man/tcl8.5/TclCmd/expr.htm#M15 for the "ni" operator.

How can I find the sum of the elements of an array in Bash?

I am trying to add the elements of an array that is defined by user input from the read -a command. How can I do that?
read -a array
tot=0
for i in ${array[#]}; do
let tot+=$i
done
echo "Total: $tot"
Given an array (of integers), here's a funny way to add its elements (in bash):
sum=$(IFS=+; echo "$((${array[*]}))")
echo "Sum=$sum"
e.g.,
$ array=( 1337 -13 -666 -208 -408 )
$ sum=$(IFS=+; echo "$((${array[*]}))")
$ echo "$sum"
42
Pro: No loop, no subshell!
Con: Only works with integers
Edit (2012/12/26).
As this post got bumped up, I wanted to share with you another funny way, using dc, which is then not restricted to just integers:
$ dc <<< '[+]sa[z2!>az2!>b]sb1 2 3 4 5 6 6 5 4 3 2 1lbxp'
42
This wonderful line adds all the numbers. Neat, eh?
If your numbers are in an array array:
$ array=( 1 2 3 4 5 6 6 5 4 3 2 1 )
$ dc <<< '[+]sa[z2!>az2!>b]sb'"${array[*]}lbxp"
42
In fact there's a catch with negative numbers. The number '-42' should be given to dc as _42, so:
$ array=( -1.75 -2.75 -3.75 -4.75 -5.75 -6.75 -7.75 -8.75 )
$ dc <<< '[+]sa[z2!>az2!>b]sb'"${array[*]//-/_}lbxp"
-42.00
will do.
Pro: Works with floating points.
Con: Uses an external process (but there's no choice if you want to do non-integer arithmetic — but dc is probably the lightest for this task).
My code (which I actually utilize) is inspired by answer of gniourf_gniourf. I personally consider this more clear to read/comprehend, and to modify. Accepts also floating points, not just integers.
Sum values in array:
arr=( 1 2 3 4 5 6 7 8 9 10 )
IFS='+' sum=$(echo "scale=1;${arr[*]}"|bc)
echo $sum # 55
With small change, you can get the average of values:
arr=( 1 2 3 4 5 6 7 8 9 10 )
IFS='+' avg=$(echo "scale=1;(${arr[*]})/${#arr[#]}"|bc)
echo $avg # 5.5
gniourf_gniourf's answer is excellent since it doesn't require a loop or bc. For anyone interested in a real-world example, here's a function that totals all of the CPU cores reading from /proc/cpuinfo without messing with IFS:
# Insert each processor core count integer into array
cpuarray=($(grep cores /proc/cpuinfo | awk '{print $4}'))
# Read from the array and replace the delimiter with "+"
# also insert 0 on the end of the array so the syntax is correct and not ending on a "+"
read <<< "${cpuarray[#]/%/+}0"
# Add the integers together and assign output to $corecount variable
corecount="$((REPLY))"
# Echo total core count
echo "Total cores: $corecount"
I also found the arithmetic expansion works properly when calling the array from inside the double parentheses, removing the need for the read command:
cpuarray=($(grep cores /proc/cpuinfo | awk '{print $4}'))
corecount="$((${cpuarray[#]/%/+}0))"
echo "Total cores: $corecount"
Generic:
array=( 1 2 3 4 5 )
sum="$((${array[#]/%/+}0))"
echo "Total: $sum"
I'm a fan of brevity, so this is what I tend to use:
IFS="+";bc<<<"${array[*]}"
It essentially just lists the data of the array and passes it into BC which evaluates it. The "IFS" is the internal field separate, it essentially specifies how to separate arrays, and we said to separate them with plus signs, that means when we pass it into BC, it receives a list of numbers separated by plus signs, so naturally it adds them together.
Another dc & bash method:
arr=(1 3.88 7.1 -1)
dc -e "0 ${arr[*]/-/_} ${arr[*]/*/+} p"
Output:
10.98
The above runs the expression 0 1 3.88 7.1 _1 + + + + p with dc. Note the dummy value 0 because there's one too many +s, and also note the usual negative number prefix - must be changed to _ in dc.
arr=(1 2 3) //or use `read` to fill the array
echo Sum of array elements: $(( ${arr[#]/%/ +} 0))
Sum of array elements: 6
Explanation:
"${arr[#]/%/ +}" will return 1 + 2 + 3 +
By adding additional zero at the end we will get 1 + 2 + 3 + 0
By wrapping this string with BASH's math operation like this$(( "${arr[#]/%/ +} 0")), it will return the sum instead
This could be used for other math operations.
For subtracting just use - instead
For multiplication use * and 1 instead of 0
Can be used with logic operators too.
BOOL AND EXAMPLE - check if all items are true (1)
arr=(1 0 1)
if [[ $((${arr[#]/%/ &} 1)) -eq 1 ]]; then echo "yes"; else echo "no"; fi
This will print: no
BOOL OR EXAMPLE - check if any item is true (1)
arr=(1 0 0)
if [[ $((${arr[#]/%/ |} 0)) -eq 1 ]]; then echo "yes"; else echo "no"; fi
This will print: yes
A simple way
function arraySum
{
sum=0
for i in ${a[#]};
do
sum=`expr $sum + $i`
done
echo $sum
}
a=(7 2 3 9)
echo -n "Sum is = "
arraySum ${a[#]}
I find this very simple using an increasing variable:
result2=0
for i in ${lineCoffset[#]};
do
result2=$((result2+i))
done
echo $result2

Resources