最佳方法传递变量给嵌套函数?

huangapple go评论85阅读模式
英文:

Best method for passing variables to nested functions?

问题

我通常会像这样做:

function func1 {
    param (
        $a
    )
    function func2 {
        param (
            $a
        )
        return $a
    }
    return func2 -a $a
}

但是每次嵌套函数都要重复使用param块,以及包括/移除适用的参数会变得繁琐。

我已经这样做了:

function func1 {
    param (
        $a
    )
    function func2 {
        return $a
    }
    return func2
}

但是对我来说,这似乎可能会引发问题,因为func2假定$a变量存在,而不是显式定义为func2的参数。

那么哪种方法最好,或者是否有更好的不同方法?

英文:

I typically do something like this:

function func1 {
    param (
        $a
    )
    function func2 {
        param (
            $a
        )
        return $a
    }
    return func2 -a $a
}

But it gets tedious repeating the param block for every nested function and including/removing the applicable parameters.

I've done this:

function func1 {
    param (
        $a
    )
    function func2 {
        return $a
    }
    return func2
}

But it seems to me this could invite issues since func2 assumes the $a variable exists without it being explicitly defined as a parameter for func2.

So which is the best method, or is there a different method that's better?

答案1

得分: 1

以下是您要翻译的内容:

<!-- language-all: sh -->

Note:

* [Powershell: inspect, consume and pass through arguments?](https://stackoverflow.com/questions/74710805/powershell-inspect-consume-and-pass-through-arguments) deals with the _complementary_ scenario: How to relay arguments (parameter values) to a nested or separate function or script having the _same_ (or least compatible) parameter declarations (`param(...)` block), using the [automatic `$PSBoundParameters` variable](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Automatic_Variables#psboundparameters). While this ultimately makes for the most robust solution, your intent is to _avoid this duplication_ of parameter declarations.

* The answer below contains a solution that may or may not be worth the trouble; if not, I hope that at least the background information that is provided is useful.

---

PowerShell's _dynamic scoping_ makes all variables in ancestral scopes (in the same scope domain aka "session state")<sup>[1]</sup> on the call stack visible to 
a given scope.<sup>[2]</sup>

* Only variables defined with the `$private:` (pseudo) scope are exempt from dynamic scoping; that is, this [scope specifier](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Scopes#scope-modifiers) makes a variable visible to the local scope only, but not to any descendent ones.

Therefore, your nested `func2` function implicitly sees any variables - including _parameter_ variables - defined in the enclosing `func1` function, as well as - potentially - any variables from ancestral
scopes higher up on the call stack.

If you want to **ensure that your nested `func2` function only operates on _parameter_ variables passed to the *enclosing function*** - i.e. only on _arguments_ (parameter values) passed to `func1` , as opposed to incidental variables of the same name having been defined in a higher ancestral scope - 
you can combine dynamic scoping with the [automatic `$PSBoundParameters` variable](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Automatic_Variables#psboundparameters), which is a dictionary of the names of all explicitly bound parameters and their values.

Specifically, you can **define a local variable in the enclosing function that stores a reference to its `$PSBoundParameters` value**, which any nested scope can then reference thanks to dynamic scoping.
(Note that each nested function sees a _separate_  `$PSBoundParameters` instance in its own scope, reflecting _its_ bound parameters, if any; if a function declares no parameters, the dictionary is _empty_.)

The **advantage** of this approach is that it **neither requires parameter declarations nor passing arguments to the nested functions _while still ensuring that only parameter values passed to the parent function are operated on_**. However, in the nested functions this **requires disciplined access to the enclosing function&#39;s parameter values via the local variable referencing the enclosing function&#39;s `$PSBoundParameters` dictionary**.

Note that `$PSBoundParameters` fundamentally only tells you which parameters were bound _explicitly_, i.e. with arguments (values) _supplied by the caller_; it doesn&#39;t reflect parameter _default_ values - see [GitHub issue #3285](https://github.com/PowerShell/PowerShell/issues/3285) for a discussion.

However, parameters with default values invariably result in a local (parameter) variable with that default value, which can therefore be referenced _directly_ in a nested scope. If you know a variable
with a given name to be such a parameter variable with default value, its value is guaranteed to be the one from the enclosing scope (barring shadowing via a local variable in a nested scope).

To spell the solution out in the context of your example:

```powershell
function func1 {
  param (
      $a,
      $b = 42
  )

  # Store a reference to this function&#39;s explicitly bound parameters
  # in a local variable.
  $parentBoundParameters = $PSBoundParameters
  
  function func2 {
    # This nested function now sees the local variable from the enclosing
    # scope.
    # Output the value passed to parameter -a of the enclosing function.
    # Make sure that you reference all parameter variables that
    # don&#39;t have default values this way.
    'a parameter value (if bound): ' + $parentBoundParameters.a
    
    # A parameter bound with a *default* value is not stored in 
    # $PSBoundParameters, but is guaranteed to have a value in its scope,
    # which this nested scope sees too:
    'b parameter value (possibly bound by default value): ' + $b
  }

  # Invoke func2 *without arguments*.
  return func2

}

# Invoke the outer function with (only) an -a argument
func1 -a hi

Note that func2 does not use return and instead relies on PowerShell's implicit output behavior to "return" two strings.
return is only needed for flow control in PowerShell, though, as syntactic sugar, it can optionally be combined with producing output - see this answer.

The above produces the following output:

a parameter value (if bound): hi
b parameter value (possibly bound by default value): 42

[1] All code executing outside of modules executes in the same scope domain, whereas each module operates in its own scope domain that is connected to the global scope only - see the bottom section of this answer for details.

[2] However, with name-only variable references (e.g. $var), any scope can shadow an ancestral scope's variables of the same name (and in the same scope domain) by defining a local variable with the same name, which is happens by default on assignment (e.g. $var = ...) - see the bottom section of this answer for a concise overview of variable scopes in PowerShell.


希望这能帮助您。

<details>
<summary>英文:</summary>

&lt;!-- language-all: sh --&gt;

Note:

* [Powershell: inspect, consume and pass through arguments?](https://stackoverflow.com/questions/74710805/powershell-inspect-consume-and-pass-through-arguments) deals with the _complementary_ scenario: How to relay arguments (parameter values) to a nested or separate function or script having the _same_ (or least compatible) parameter declarations (`param(...)` block), using the [automatic `$PSBoundParameters` variable](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Automatic_Variables#psboundparameters). While this ultimately makes for the most robust solution, your intent is to _avoid this duplication_ of parameter declarations.

* The answer below contains a solution that may or may not be worth the trouble; if not, I hope that at least the background information that is provided is useful.

---

PowerShell&#39;s _dynamic scoping_ makes all variables in ancestral scopes (in the same scope domain aka &quot;session state&quot;)&lt;sup&gt;[1]&lt;/sup&gt; on the call stack visible to 
a given scope.&lt;sup&gt;[2]&lt;/sup&gt;

* Only variables defined with the `$private:` (pseudo) scope are exempt from dynamic scoping; that is, this [scope specifier](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Scopes#scope-modifiers) makes a variable visible to the local scope only, but not to any descendent ones.

Therefore, your nested `func2` function implicitly sees any variables - including _parameter_ variables - defined in the enclosing `func1` function, as well as - potentially - any variables from ancestral
scopes higher up on the call stack.

If you want to **ensure that your nested `func2` function only operates on _parameter_ variables passed to the *enclosing function*** - i.e. only on _arguments_ (parameter values) passed to `func1` , as opposed to incidental variables of the same name having been defined in a higher ancestral scope - 
you can combine dynamic scoping with the [automatic `$PSBoundParameters` variable](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Automatic_Variables#psboundparameters), which is a dictionary of the names of all explicitly bound parameters and their values.

Specifically, you can **define a local variable in the enclosing function that stores a reference to its `$PSBoundParameters` value**, which any nested scope can then reference thanks to dynamic scoping.
(Note that each nested function sees a _separate_  `$PSBoundParameters` instance in its own scope, reflecting _its_ bound parameters, if any; if a function declares no parameters, the dictionary is _empty_.)

The **advantage** of this approach is that it **neither requires parameter declarations nor passing arguments to the nested functions _while still ensuring that only parameter values passed to the parent function are operated on_**. However, in the nested functions this **requires disciplined access to the enclosing function&#39;s parameter values via the local variable referencing the enclosing function&#39;s `$PSBoundParameters` dictionary**.

Note that `$PSBoundParameters` fundamentally only tells you which parameters were bound _explicitly_, i.e. with arguments (values) _supplied by the caller_; it doesn&#39;t reflect parameter _default_ values - see [GitHub issue #3285](https://github.com/PowerShell/PowerShell/issues/3285) for a discussion.

However, parameters with default values invariably result in a local (parameter) variable with that default value, which can therefore be referenced _directly_ in a nested scope. If you know a variable
with a given name to be such a parameter variable with default value, its value is guaranteed to be the one from the enclosing scope (barring shadowing via a local variable in a nested scope).

To spell the solution out in the context of your example:

function func1 {
param (
$a,
$b = 42
)

Store a reference to this function's explicitly bound parameters

in a local variable.

$parentBoundParameters = $PSBoundParameters

function func2 {
# This nested function now sees the local variable from the enclosing
# scope.
# Output the value passed to parameter -a of the enclosing function.
# Make sure that you reference all parameter variables that
# don't have default values this way.
'$a parameter value (if bound): ' + $parentBoundParameters.a

# A parameter bound with a *default* value is not stored in 
# $PSBoundParameters, but is guaranteed to have a value in its scope,
# which this nested scope sees too:
&#39;$b parameter value (possibly bound by default value): &#39; + $b

}

Invoke func2 without arguments.

return func2

}

Invoke the outer function with (only) an -a argument

func1 -a hi


Note that `func2` does _not_ use `return` and instead relies on PowerShell&#39;s implicit output behavior to &quot;return&quot; two strings.
`return` is only needed for flow control in PowerShell, though, as syntactic sugar, it can _optionally_ be combined with producing output - see [this answer](https://stackoverflow.com/a/72663713/45375).

The above produces the following output:

```none
$a parameter value (if bound): hi
$b parameter value (possibly bound by default value): 42

<sup>[1] All code executing outside of modules executes in the same scope domain, whereas each module operates in its own scope domain that is connected to the global scope only - see the bottom section of this answer for details.</sup>

<sup>[2] However, with name-only variable references (e.g. $var), any scope can shadow an ancestral scope's variables of the same name (and in the same scope domain) by defining a local variable with the same name, which is happens by default on assignment (e.g. $var = ...) - see the bottom section of this answer for a concise overview of variable scopes in PowerShell.</sup>

huangapple
  • 本文由 发表于 2023年8月5日 05:30:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/76839188.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定