adding numbers returned from function in powershell - powershell

I have the following basic code in powershell where I'm calculating the value of x
function add3([int]$num) {
return ($num + 3);
}
[String]$argA = "AB";
[int]$x = (add3($argA.Length) + 2);
Write-Host($x)
Running this in ISE I'm getting the value 5 instead of 7.

In PowerShell, you call functions by listing the arguments separated by spaces, not commas, and parentheses are not used. This is a common source of confusion. Your expression:
[int]$x = (add3($argA.Length) + 2);
Is a call to the function add3 with three arguments: ($argA.Length) and the strings + and 2. Since your function has only one argument, the result is 2 + 3 = 5 and the other two are discarded (calling a function with extraneous parameters is not an error in PowerShell). You can verify this is what's happening by changing your function:
function add3([int]$num, $y, $z) {
Write-Host $y;
Write-Host $z;
return ($num + 3);
}
The solution is to parenthesize the expression properly:
$x = (add3 $argA.Length) + 2;

You're getting the correct output.
Try to use this approach instead:
[int]$x = (add3($argA.Length)) + 2

function add3($num) {
return ($num + 3);
}
[String]$argA = "AB";
$x = (add3($argA.Length)) +2
Write-Host($x)

Related

Invoke arithmetic operator dynamically

Powershell has arithmetic operators like addition (+), substraction (-) and bitwise and (-band). I'm making a simple calculator program, and I want to dynamically perform arithmetic calculations based on the user input, without needing to write a lot of if-else statements. Is there a way to dynamically invoke powershell operators? E.g. if $method=="plus" do "6+6".
I know there is Invoke-Expression, but that doesn't really operate on the operator alone (you also need to supply the operands in the expression string). Is there some way to define the operator as a variable? E.g. $method="-band", $result=6 $method 6;
you can use like this way
$no1 = 10 ; $no2 = 5 ; # your inputs
function plus($one , $two) # plus operation method
{
$ans = $no1 + $no2 ;
Write-Output $ans
}
function minus($one , $two) # minus operation method
{
$ans = $no1 - $no2 ;
Write-Output $ans
}
$method = "plus" # dyn. method name
& "$method" 10 5 # Call method with param
$method = "minus" ; # dyn. method name
& "$method" 10 5 # Call method with param

Can a function be used in a equation

Is there a way to call a function when using an equation within Powershell.
I am trying to something like the following however the last line returns an error You must provide a value expression....
function quote($str) {
return """" + $str + """";
};
$a= "abc: " + quote('hi'); # <-- Doesn't Work
I realize I could assign the quote to an intermediate variable and then do the concatenation ($q=quote('hi'); $a="abc: " + q$) however I am hoping there is a simpler syntax.
Is this what you mean:
function quote($str) {
return """" + $str + """";
};
$a= "abc: " + $(quote('hi'));
# edit: as per Joey's comment, this will also work:
$a= "abc: " + (quote('hi'));
Edit
Re-written using PowerShell syntax:
# Function name is verb-noun, from approved verbs
# https://msdn.microsoft.com/en-us/library/ms714428(v=vs.85).aspx
Function Add-Quote{
# parameters this function takes
param([string]$str)
# No need for return and semi colon.
# I tend to use return as it makes my code reading easier
"""" + $str + """"
}
$a = "abc: " + (Add-Quote -str 'hi')
You could use the format operator -f to insert the string.
function quote {
param($str)
"`"$str`""
}
$a = 'abc: {0}' -f (quote 'hi')

Convert string to int array in PowerShell

I'm trying to convert a string that looks like this,
2,3,4,5,6,20..30
to an array of integers. Here is the code I currently have:
[string]$a = "2,3,4,5,6,7"
[array]$b = $a.split(",")
[array]$c = foreach($number in $b) {([int]::parse($number))}
Which works, but not for the range of 20..30. How do I get that part working?
You can use the Invoke-Expression cmdlet to interpret the 10..30 bit, if the [int]::Parse() method call fails.
Here is a complete, working sample.
[string]$a = "2,3,4,5,6,7,10..30";
[array]$b = $a.split(",");
[array]$c = foreach($number in $b) {
try {
[int]::parse($number)
}
catch {
Invoke-Expression -Command $number;
}
}
$c;
One-liner (just for fun):
$c = "2,3,4,5,6,7,10..30".split(',') | % {iex $_}
Inside of a string, your .. is taken as-is and is not expanded to a range.
So $a = "1 .. 5" is actually 1 .. 5 and not 1, 2, 3, 4, 5.
To make your program work, you will have to tokenize not only on , but also on .. and then expand.

pass 3 value to sub : Too many arguments for

I have an input like this:
100 200 A_30:120,A_140:180,B_180:220
100 300 A_70:220,B_130:300,A_190:200,A_60:300
I want to count number of A or B in each line and also compare range of A or B in each line with the range in two first column and return the length of intersection. e.g. output for first line: A:2 A_length:40 B:1 B_length:20
while(<>){
chomp($_);
my #line = split("\t| ", $_);
my $col1 = $line[0];
my $col2 = $line[1];
my #col3 = split(",",$line[2]);
my $A=0;
my $B=0;
my $A_length=0;
my $B_length=0;
for my $i (0 .. #col3-1){
my $col3 = $col3[$i];
if ($col3 =~ /A/){
my $length=0;
$length = range ($col3,$col1,$col2);
$A_length = $A_length+$length;
$A++;
}
if ($col3 =~ /B/){
my $length=0;
$length = range ($col3,$col1,$col2);
$B_length = $B_length+$length;
$B++;
}
$i++;
}
print("#A: ",$A,"\t","length_A: ",$A_length,"\t","#B: ",$B,"\t","length_B: ",$B_length,"\n");}
sub range {
my ($col3, $col1, $col2) = ($_[0],$_[1],$_[2]);
my #sub = split(":|_", $col3);
my $sub_strt = $sub[1];
my $sub_end = $sub[2];
my $sub_length;
if (($col1 >= $sub_strt) && ($col2 >= $sub_end)){
$sub_length = ($sub_end) - ($col1);}
if (($col1 >= $sub_strt) && ($col2 >= $sub_end)){
$sub_length = ($col2) - ($col1);}
if(($col1 <= $sub_strt) && ($col2 >= $sub_end)){
$sub_length = ($sub_end) - ($sub_strt);}
if(($col1 <= $sub_strt) && ($col2 <= $sub_end)){
$sub_length = ($col2) - ($sub_strt);}
return $sub_length;
}
I FIXED IT :)
Perl already has a builtin length function, which only takes one argument. As perl is compiling your script and gets to your length function call, it doesn't know about the sub length { ... } that you have defined later in the script, so it complains that you are using the builtin length function incorrectly.
How to fix this? This is Perl, so there are many ways
name your function something else. Making a function with the same name as a Perl builtin function is usually a bad idea
Call your function with the & sigil: my $length = &length($col3,$col1,$col2); That will be enough of a hint to the compiler that your function call does not refer to the builtin function.
Qualify your function call with a package name, in this case main::length($col3,$col1,$col2) or just ::length($col3,$col1,$col2).
Note that even if Perl did know about the length function you defined (you could get Perl to know by moving the sub length { ... } definition to the top of the script, for example), the function call would still be ambiguous to the compiler, the compiler would emit a warning like
Ambiguous call resolved as CORE::length(), qualify as such or use & at ...
and your script would still fail to compile. Here CORE::length would mean that Perl is resolving the ambiguity in favor of the builtin function.

Using perl's reduce to compute dot-product

Suppose I have the following two equal-sized arrays in my perl program:
my #arr1 = 1..5;
my #arr2 = 6..10;
I'm trying to get their dot-product using the reduce function defined in the List::Util core module but the following is not working for me:
my $dot_prod = reduce { $arr1[$a] * $arr2[$a] + $arr1[$b] * $arr2[$b] }0..$#arr1;
I get 50 as my output instead of the expected 130.
The documentation describes the behavior of reduce as follows:
The first call will be with $a and $b set to the first two elements of
the list, subsequent calls will be done by setting $a to the result of
the previous call and $b to the next element in the list.
Thus, in this case, on the first iteration reduce will set $a = 0 and $b = 1, and hence, execute
$arr1[0] * $arr2[0] + $arr1[1] * $arr2[1]
This temporary result happens to be 20.
Now, for the second iteration, $a is set to the result of the previous iteratrion and so $a = 20 and $b = 2. Thus, the following will be executed
$arr1[20] * $arr2[20] + $arr1[2] * $arr2[2]
which is not what we want.
A possible workaround:
prepend an initial 0 to the list provided as input to reduce as follows:
my $dot_prod = reduce { $a + $arr1[$b] * $arr2[$b] } 0, 0..$#arr1;
This gives us the desired result since on the first iteration $a = $b = 0 and we'll compute
0 + $arr[0] * $arr[0]
whose result will be 6.
Then in the second iteration, we'll have $a = 6 $b = 1 and so we'll compute
6 + $arr1[1] * $arr2[1]
etc.
Honestly,
my $dot_prod = reduce { $a + $arr1[$b] * $arr2[$b] } 0, 0..$#arr1;
isn't the most readable. There is this:
my $dot_prod = sum map { $arr1[$_]*$arr2[$_] } 0..$#arr1;
But that doesn't use reduce. Well, we could simply implement sum in terms of reduce instead of using List::Util's, and perhaps even inline it:
my $dot_prod = reduce { $a+$b } map { $arr1[$_]*$arr2[$_] } 0..$#arr1;
Here are the previously posted solutions in a runnable program:
my #arr1 = 1..3;
my #arr2 = 6..8;
use List::Util qw(reduce sum) ;
my $dot_prod0 = reduce { $a + $arr1[$b] * $arr2[$b] } 0,0..$#arr1; #reduce
print "Dot product0 = ".$dot_prod0."\n";
my $dot_prod1 = sum map { $arr1[$_]*$arr2[$_] } 0..$#arr1; #sum map
print "Dot product1 = ".$dot_prod1."\n";
my $dot_prod2 = reduce { $a+$b } map { $arr1[$_]*$arr2[$_] } 0..$#arr1; #reduce map
print "Dot product2 = ".$dot_prod2."\n";