Given the enumeration
enum NATO (:alpha<A>, :bravo<B>, :charlie<C>, :delta<D>);
it's possible to easily set a variable by literally typing one of the names, or by passing one of the values to the enum object:
my $a = alpha;
my $b = NATO('B');
say $a; # ↪︎ alpha
say $b; # ↪︎ bravo
say $a.value; # ↪︎ A
say $b.value; # ↪︎ B
Besides using EVAL and given a Str that corresponds to one of the enums, how could I create $c to be an enum value equivalent to charlie?
my $x = 'charlie';
my $c = ...
You can treat it as a Hash:
my $c = NATO::{$x};
You can use indirect name lookup:
enum NATO (:alpha<A>, :bravo<B>, :charlie<C>);
my $x = 'charlie';
my $c = ::($x);
say $c.value;
Related
below is my scenario wherein i need to Increment by 1 in while loop when string interpolation is used in Ruby
When i do this
$min_value = 1
$max_value = 3
while $min_value < $max_value do
$url = "https:abcd.com?page=#{$min_value}&per_page=#{$max_value}"
puts $url
$min_value += 1
end
I get result like this below, which is perfect
https:abcd.com?page=1&per_page=3
https:abcd.com?page=2&per_page=3
But i want to use the $url outside the loop as a parameter
$min_value = 1
$max_value = 3
$url = "https:abcd.com?page=#{$min_value}&per_page=#{$max_value}"
while $min_value < $max_value do
$test = eval('$url')
puts $test
$min_value += 1
end
and i get wrong result
https:abcd.com?page=1&per_page=3
https:abcd.com?page=1&per_page=3
expected result is
https:abcd.com?page=1&per_page=3
https:abcd.com?page=2&per_page=3
how can i make this string interpolation of $min_value be increment. As my requirement is to keep the $url outside the loop as user will give this information
I suggest you avoid using eval on a string defined with single quotes. The conventional approach is the following.
min_value = 1
max_value = 3
fmt = "https:abcd.com?page=%s&per_page=%s"
(min_value..max_value-1).each { |n| puts sprintf(fmt, n, max_value) }
https:abcd.com?page=1&per_page=3
https:abcd.com?page=2&per_page=3
The operative line could instead be written:
(min_value..max_value-1).each { |n| puts fmt % [n, max_value] }
or
fmt1 = fmt + "\n"
#=> "https:abcd.com?page=%s&per_page=%s\n"
(min_value..max_value-1).each { |n| printf(fmt1, n, max_value) }
See Kernel#sprintf, String#% and Kernel#printf.
You should use global variables only when absolutely necessary, which often is never. Use instance variables only when a local variable will not do. I've made min_value, max_value and fmt all local variables because there is no suggestion by the question that their values are needed beyond the immediate task.
Generally, you should use loop structures while, until and loop only when the number of iterations is not known in advance. Here I've just iterated over a range.
Note that (min_value..max_value-1) could be replaced with (min_value...max_value). I only use three-dot ranges when the end of an infinite range is to be excluded. Mixing two- and three-dot ranges (in my opinion) makes code harder to read and results in more coding errors.
Change the $url to below:
$url = '"https:abcd.com?page=#{$min_value}&per_page=#{$max_value}"'
The reason your code is not working is because $url is already evaluated as string and you are trying to eval again. And, remove single quotes in eval method.
Final code:
$min_value = 1
$max_value = 3
$url = '"https:abcd.com?page=#{$min_value}&per_page=#{$max_value}"'
while $min_value < $max_value do
$test = eval($url)
puts $test
$min_value += 1
end
First, Refactor to Block-Local Variables
You've constructed a very non-idiomatic loop and applied some inconsistent formatting. As a result, you may be obscuring the fact that your interpolation is happening before you enter the loop, which means that Kernel#eval won't resolve the way you expect even if your string were properly quoted. The string is therefore never updated.
A refactoring that avoids global variables and eval looks like this:
starting_page = 1
items_per_page = 3
starting_page.upto(items_per_page) do |page|
url = "https://abcd.com?page=#{page}&per_page=#{items_per_page}"
puts url
end
This yields your expected results:
https://abcd.com?page=1&per_page=3
https://abcd.com?page=2&per_page=3
https://abcd.com?page=3&per_page=3
By defining a block-local variable (e.g. page) you also prevent unintended changes outside the block formed by Integer#upto. Understanding scope gates in Ruby has a steep learning curve, but it's often very helpful in avoiding subtle (or not-so-subtle) bugs in your code.
Next, Define a Format String Outside Your Loop
If you want to move the url variable outside the block, you will still need to generate a new string inside the loop. Eval is not ideal for this, although it can work if you fix your quoting. A better approach is to use Kernel#printf or Kernel#sprintf with a format string, depending on whether you want the output on an IO object or as a return value. For example:
starting_page = 1
items_per_page = 3
format_string = "https://abcd.com?page=%d&per_page=%d\n"
starting_page.upto(items_per_page) do |page|
printf format_string, page, items_per_page
end
This will print the same values to standard output as the loop with interpolation, but the string holding the format of the URL is now defined outside the loop while the %d placeholders in each string are replaced at runtime.
how to pass 2d array to function in shell script ?
i need to pass matrix to function but it do not work
tr(){
matrix="$3"
num_rows="$1"
num_columns="$2"
f1="%$((${#num_rows}+1))s"
f2=" %9s"
for ((i=1;i<=num_rows;i++)) do
for ((j=1;j<=num_columns;j++)) do
echo -ne "${matrix[$i,$j]}\t"
done
echo -e "\n"
done
tr $rows $columns $x
Use an associative array:
declare -A matrix
Then things like matrix[6,7]=42 will work because "6,7" ist just a string, and associative arrays accept strings as indices. You might as well write things like
matrix[one,two]=three
matrix[yet,another,dimension]="Perry Rhodan"
You can just write any string between [ and ]. Here is a complete example for how to use it.
#!/bin/bash
#
# Example for a function that accepts the name of an associative array ...
# ... and does some work on the array entries, e.g. compute their sum
# We assume that all *values* of the array are integers - no error check
sum() {
local s=0 # we don't want to interfere with any other s
declare -n local var="$1" # now var references the variable named in $1
for value in "${var[#]}" # value runs through all values of the array
do
let s+="$value"
done
echo sum is $s
}
declare -A m # now m is an associative array, accepting any kind of index
m[0,0]=4 # this looks like 2-dimensional indexing, but is is not
m[2,3]=5 # m will accept any reasonable string as an array index
m[678]=6 # m does not care about the number of commas between numbers
m[foo]=7 # m does not even care about the indices being numbers at all
sum m
As you see, the matrix m not really has 2 dimensions. It just takes any string as an index, as long as it does not contains certain shell syntax characters, and comma is allowed in the string.
Please note the reference declare -n ... - this allows simple access to the matrix from within the function and, most important, without knowing the name of the matrix. Thus you can call that function for several matrices with different names.
The keyword local is important. It means that, upon return, var is unset automatically. Otherwise you will have a reference "var" to an associative array. If you ever want to use var later, it will be hard to use it because you cannot use it as anything else but an associative array. And if you try to get rid of it by "unset var", bash will kindly remember that var refers to m, and delete your matrix m instead. In general, make variables in functions be local wherever possible. It even allows me to re-use a name. For example, using "s" as a variable name inside a function may appear dangerous because it might change the value of a global variable "s". But it doesn't - by declaring it local, the function has its own private variable s, and any s that might already exist is untouched.
Just as a demonstration: If you want to see the array indices in the loop, do this:
sum() {
local s=0 # we don't want to interfere with any other s
declare -n local var="$1" # now var references the variable named in $1
for i in "${!var[#]}" # !var means that we run through all indices
do # we really need a reference here because ...
let s+=${var["$i"]} # ... indirections like ${!$1[$i]} won't work
done
echo sum is $s
}
I am intending to change a global variable inside a function in BASH, however I don't get a clue about how to do it. This is my code:
CANDIDATES[5]="1 2 3 4 5 6"
random_mutate()
{
a=$1 #assign name of input variable to "a"
insides=${!a} #See input variable value
RNDM_PARAM=`echo $[ 1 + $[ RANDOM % 5 ]]` #change random position in input variable
NEW_PAR=99 #value to substitute
ARR=($insides) #Convert string to array
ARR[$RNDM_PARAM]=$NEW_PAR #Change the random position
NEW_GUY=$( IFS=$' '; echo "${ARR[*]}" ) #Convert array once more to string
echo "$NEW_GUY"
### NOW, How to assign NEW_GUY TO CANDIDATES[5]?
}
random_mutate CANDIDATES[5]
I would like to be able to assign NEW_GUY to the variable referenced by $1 or to another variable that would be pointed by $2 (not incuded in the code). I don't want to do the direct assignation in the code as I intend to use the function for multiple possible inputs (in fact, the assignation NEW_PAR=99 is quite more complicated in my original code as it implies the selection of a number depending the position in a range of random values using an R function, but for the sake of simplicity I included it this way).
Hopefully this is clear enough. Please let me know if you need further information.
Thank you,
Libertad
You can use eval:
eval "$a=\$NEW_GUY"
Be careful and only use it if the value of $a is safe (imagine what happens if $a is set to rm -rf / ; a).
I read somewhere that ksh's array supports += to append new elements, but I tried it and it doesn't work:
[ksh] # arr=(a b c d)
[ksh] # arr+=e
[ksh] # echo ${arr[*]}
ae b c d
[ksh] #
Why does arr[0] becomes ae?
To add an element to the array, it should be like this:
arr+=(e)
By doing arr+=e , it will add to the 1st element of the array. Its because just the name arr points to the 1st element of the array itself:
$ arr=(a b c d)
$ echo ${arr[0]}
a
$ echo $arr
a
It's arr+=(e). Any simple assignment that doesn't specify an index always refers to the zeroth element.
Note that this is a trivial case and things can get more complicated. += has different behavior for both simple and compound assignment depending on context, and also differs between bash, ksh93, and zsh, so it's easy to become confused.
http://wiki.bash-hackers.org/syntax/arrays#storing_values
I am looing for information on google about declaring array in expect but unable to find it.even the witki link for the line is empty.
I know i can set array values like set arr("hh") "hhh" but how do i declare it.
and can i print the whole array using one command or do i have to loop through it to print all the elements.
Or there is no such thing as declaring array in expect/tcl.i mean can we access any array
just by using global keyword.
You don't have to declare an array, but if you want to:
array set variableName {}
The last word is an empty list. If you have some default values you want to store in the array, you can say:
array set varname {key1 val1 key2 val2 ... ...}
If you're curious, here's how parray is implemented:
proc parray {a {pattern *}} {
upvar 1 $a array
if {![array exists array]} {
error "\"$a\" isn't an array"
}
set maxl 0
set names [lsort [array names array $pattern]]
foreach name $names {
if {[string length $name] > $maxl} {
set maxl [string length $name]
}
}
set maxl [expr {$maxl + [string length $a] + 2}]
foreach name $names {
set nameString [format %s(%s) $a $name]
puts stdout [format "%-*s = %s" $maxl $nameString $array($name)]
}
}
You don't declare arrays in Expect (or Tcl in general) you just use them.
But arrays and other variables do have scope. If you are in a proc and want to
refer to an array arr which has global scope you can either say global arr before
using it or prefix the name with :: each time you use it, eg. set ::arr(hh) "hhh"; puts $::arr(hh).
There is a command parray to print a whole array, but this is loaded from library scripts rather than being built-in, so may not be available depending on how your Expect installation has been done. Eg.
expect1.1> set arr(a) ACBD
ACBD
expect1.2> set arr(b) "BBB bbb"
BBB bbb
expect1.3> parray arr
arr(a) = ACBD
arr(b) = BBB bbb