I wrote a method that can be recursive, but the return value is always 0, even when it has a value as a string on the console:
Func hasItTheThing($s)
$result = StringInStr(...,...)
local $newstring
$newstring = $s
If NOT $result > 0 Then
ConsoleWrite("newstring = " & $newstring & #CRLF)
return $newstring
Else
$newstring = ;Fix something with the string
hasItTheThing($newstring)
EndIf
EndFunc
Also, your bug that caused the always zero result: The "Else" branch of your IF (If NOT $result > 0) has no return. So when result > 0, your function calls itself but when that returns a value, it is discarded and the function continues to the exit, returning nothing.
The fix:
...
Else
$newstring = ;Fix something with the string
return hasItTheThing($newstring) ; <== Added a "return" here
EndIf
EndFunc
Ah, nevermind, http://www.autoitscript.com/wiki/Recursion
answered this.
I solved it by replacing the keyword return with a local variable that is declared outside the function, and set the value to this variable.
Related
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)
Hey guys i´m just starting with powershell, now I´ve got this problem. I try to check wheater the last character of a inputed path is a "\". But when I run this code the variable $lastRootPathChar is empty. The $end and $start variables get the integers I want them to have, but the .Substring seems to return nothing. What am I doing wrong?
$RootPath = Read-Host 'Please enter the rootpath: '
$start = $RootPath.Length-1
$end = $RootPath.Length
$lastRootPathChar = $RootPath.Substring($start,$end)
if($lastRootPathChar -ne "\")
{
$RootPath = $RootPath + "\"
}
The two parameters passed to that overload of String.Substring are start and length not start and end
$lastRootPathChar = $RootPath.Substring($start,1)
The second argument to "".Substring() is the total length of the desired substring, not the end index.
$LastChar = $RootPath.Substring($start, 1)
You could also use the index operator [] to access the last char in the string (index -1):
$LastChar = $RootPath[-1] -as [string]
$lastRootPathChar = $RootPath.Substring($RootPath.length -1)
This will get the slash out. As $RootPath.length -1 is the index of the last character and there are no characters after that.
I am simply trying to increment a value within the ternary operator's 'true' section when the test() subroutine returns true, but set it back to zero when the it returns false.
I don't understand why the following code doesn't work:
use warnings;
use strict;
my $int = 5;
test() ? $int += 1 : $int = 0;
print "$int\n"; # => prints "0"
sub test {
return 1; #return true
}
The answer is fairly simple: perl's ternary operator is assignable
In particular, your code really means this:
(test() ? $int += 1 : $int) = 0;
You may recall that the result of an assignment is the variable being assigned to; this means that the following happens:
test() evaluates to true
$int += 1 is evaluated, incrementing $int to 6
The result of the assignment, $int, is assigned the value 0
This code will work how you want:
test() ? ($int++) : ($int = 0);
Or this:
$int += test() ? 1 : -$int; #If you want to be cute
& {
$action = & { $y = 100
return { write-host "Value: $y" }.getnewclosure()
}
[void] (register-engineevent -sourcei "foo" -action $action)
[void] (new-event -sourcei "foo")
}
The code above prints Value:, while I'd expect it to print Value: 100. Is something missing here?
The engine is calling GetNewClosure() on it, which is a better thing for it to do than not.
When I ran the above, I got an output of only Value:, but then I globally declared $y = 25 and ran [void](new-event -sourcei 'foo') again, the output was Value: 25. Then I changed its value again, $y = 38, and ran the event again: Value: 38.
If what you want is to bake in the value of $y at the time you create the action, one way to do that is to create the scriptblock in such a way that the value of $y becomes a literal or part of a literal:
$action = &{ $y = 100
return (invoke-expression "{write-host 'Value: $y'}")
}
This first parses the string to insert the value, so Invoke-Expression ends up doing the equivalent of this:
{write-host 'Value: 100'}
I'm not sure if there are any other ways to bake in the value other than composing the entire content of the scriptblock in a string and passing it through Invoke-Expression.
In response to your comment, further explanation of closures:
> $action100 = &{$y = 100; return {write-host "Value: $y"}.GetNewClosure()}
> &$action100
Value: 100
That's the result you expect, and that's what you get, because the scriptblock is still "closed" around the value of $y in the scope where GetNewClosure() was called.
> $y = 25
> &$action100
Value: 100
Still closed around the $y that is 100.
> $action25 = $action100.GetNewClosure()
> &$action25
Value: 25
This produces a new scriptblock that encloses variables in the current scope. It makes it re-evaluate what $y is in that scriptblock, and in this context, $y is now 25.
> $y = 38
> &$action100
Value: 100
> &$action25
Value: 25
> &($action100.GetNewClosure())
Value: 38
At this point, because $y is declared globally now, when you call New-Event it will use GetNewClosure() and re-evaluate $y to 38, and print Value: 38. You've been getting Value: because in the context where the engine events call GetNewClosure() the variable $y is not defined, so "Value: $y" becomes "Value: ".
Why are the variable assignments that I make inside the Trap block not visible outside it?
$integer = 0;
$string = [String]::Empty;
$stringBuilder = new-object 'System.Text.StringBuilder';
trap
{
$integer = 1;
$string = '1';
$stringBuilder.Append('1');
write-host "Integer Variable Inside: " $integer;
write-host "String Variable Inside: " $string;
write-host "StringBuilder Variable Inside: " $stringBuilder;
continue;
}
$dummy = 1/$zero;
write-host "Integer Variable Outside: " $integer;
write-host "String Variable Outside: " $string;
write-host "StringBuilder Variable Outside: " $stringBuilder;
I would have expected the results from within and outside the Trap block to be identical but these are the results that I am seeing.
Integer Variable Inside: 1
String Variable Inside: 1
StringBuilder Variable Inside: 1
Integer Variable Outside: 0
String Variable Outside:
StringBuilder Variable Outside: 1
Notice that it is only the StringBuilder that retains its value.
I am guessing that this has something to do with the difference between value and reference types but can't quite pin it down.
With info that slipsec provided above and through some further experimentation, I now understand what is happening here.
Joel explains how the Trap scope works as follows.
Even though in our error handler we
were able to access the value of
$Result and see that it was True … and
even though we set it to $False, and
printed it out so you could see it was
set … the function still returns True,
because the trap scope doesn’t modify
the external scope unless you
explicitly set the scope of a
variable. NOTE: If you had used
$script:result instead of $result (in
every instance where $result appears
in that script), you would get the
output which the string/comments led
you to expect.
So variables from outside the Trap scope can be read but not set because they are copies of the originals (thanks Jason). This is the reason why the Integer variable did not retain its value. The StringBuilder however, is a reference object and the variable is only a pointer to that object. The code within the Trap scope was able to read the reference that the variable was set to and modify the object to which it was pointing - the variable itself required no change.
Note that Joel's tip about specifying the scope of the variable allowed me to set the value of the Integer variable from within the Trap scope.
$script:integer = 0;
$string = [String]::Empty;
$stringBuilder = new-object 'System.Text.StringBuilder';
trap
{
$script:integer = 1;
$string = '1';
$stringBuilder.Append('1');
write-host "Integer Variable Inside: " $script:integer;
write-host "String Variable Inside: " $string;
write-host "StringBuilder Variable Inside: " $stringBuilder;
continue;
}
$dummy = 1/$zero;
write-host "Integer Variable Outside: " $script:integer;
write-host "String Variable Outside: " $string;
write-host "StringBuilder Variable Outside: " $stringBuilder;
...and these are the results.
Integer Variable Inside: 1
String Variable Inside: 1
StringBuilder Variable Inside: 1
Integer Variable Outside: 1
String Variable Outside:
StringBuilder Variable Outside: 1
Note that the string variable does not retain its value because although it is a reference type, it is also immutable.
Rather than re-write Jakul's excelent post on the subject, I'll just link it:
http://huddledmasses.org/trap-exception-in-powershell/
Gobs of information on how powershell deals with error handling with plenty of detail.