Scope and Functions

by mipbar at 2013-01-14 12:02:10

I"m using functions as basic code separation, so I can call the same block of code from multiple locations.
I’ve always had problems with scope, and need to know how to pull variables back OUT of the functions to the main block of code.

In the past, I’ve been setting all the variables I need to be both available inside and outside functions using script:variable.

I figured out how to pass variables into functions, but then it seems to be a black hole, and I can’t figure out how to pull them back out again. I tried "return $a" but that doesn’t seem to work. I tried creating functions with the scope "script:function", but that doesn’t work.

Any help please?

Here’s a small test I was playing with

$a = import-csv somefilewithlines.csv

function moo($b) {
write-host $b -foregroundcolor red
$b = $.sam
write-host $b -foregroundcolor cyan
# return $b
}

$b = ‘asdf’
$a | %{
moo($b) #with or without parens
$b
}

I get a column of

RED ‘asdf’
CYAN value of $
.sam
‘asdf’
by Lembasts at 2013-01-14 13:00:27
I had exactly the same issue and discovered what I think is the best solution - dot sourcing.
This is where you simply put a dot in front of your function call. It keeps everything in the same scope and you dont have to bother with script:variable.

So in your case it would be:
. moo $b

In fact forget the $b - just call . moo and $b will be automatically passed.
by mipbar at 2013-01-14 13:31:44
Ah thank you! I’ll give it a shot
by nohandle at 2013-01-14 22:57:08
[quote="mipbar"]I’ve always had problems with scope, and need to know how to pull variables back OUT of the functions to the main block of code.[/quote]
No, no, no! That is not what functions are for :frowning:
If you define a new function you do that to hide the (sometimes complicated) code. To simplify the operation to simple "interface".

For example you create a function named multiply (to multiply two numbers). The parameters are X and Y. You implement the body of the function and test if it produces correct output by sending it reasonable amount of test cases (the implementation is pretty easy so testing if x=2 y=2 returns 4 and x=10 y=2 is 20 should be enough for me).
Now you could forget everything you know about the implementation and still be able to use the function.

The function can be implemented like this:
function Multiply ([int]$X, [int]$Y)
{
$x*$y
}

or like this
function Multiply ([int]$X, [int]$Y)
{
$temp = 0
for ($index = 0; $index -lt [Math]::Abs($Y); $index++)
{
$temp += $X
}
if ($y -lt 0)
{
-$temp
}
else
{
$temp
}
}

the important thing is, as long as it returns correct results, you don’t have to care about the implementation. You can realize you messed up by the second implementation and replace it with the first without problem.
BUT if you implement it and use it like this
function Multiply ([int]$X, [int]$Y)
{
$temp = 0
for ($index = 0; $index -lt [Math]::Abs($Y); $index++)
{
$temp += $X
}
if ($y -lt 0)
{
$script:MultiplyResult = -$temp
}
else
{
$script:MultiplyResult = $temp
}
}

multiply 10 -10
$script:multiplyResult

you have to care about the implementation everywhere in the code.
by nohandle at 2013-01-14 23:09:42
The main idea is to split the problem into smaller parts connected by defined inputs and outputs and implement these separately. Enabling you to focus just to the one problem at the time, not caring about its surroundings. For example you want to read two numbers from user, multiply them and show the user the result. This forms three separate problems.

You could start working from the first problem but you are not sure if the numbers will be taken from keyboard, file, or database. And you don’t have to care because you know the function will return array of two integers as result. Not $script:databaseData in case it read data from database and $global:NumsToSendToOutput if the data are from file.

If you need to return more than one variable from the function you are probably doing more than one thing in the function - consider rewriting it in more specialized functions or if the data are related wrap them in object (like returning filename, filepath, size).