$ prompt =($ defaultValue,$ prompt)[[bool] $ prompt] - 在PowerShell中模拟三元条件

时间:2016-11-30 15:29:10

标签: powershell ternary-operator null-coalescing-operator

我正在学习使用PowerShell编写脚本,我发现这段代码可以帮助我完成项目示例来自Is there a one-liner for using default values with Read-Host?

$defaultValue = 'default'

$prompt = Read-Host "Press enter to accept the default [$($defaultValue)]"

$prompt = ($defaultValue,$prompt)[[bool]$prompt]

我想我明白$prompt = ($defaultValue,$prompt)正在创建一个双元素数组,而[bool]部分正在强制$prompt数据类型为布尔值,但我不明白这是什么第三行代码作为一个整体。

3 个答案:

答案 0 :(得分:4)

这是一种常见的编程模式:

if (user entered a price)
{
    price = user entered value
} 
else
{
    price = default value
}

并且由于这种情况非常普遍,而且冗长,一些语言有一个特殊的ternary operator来更简洁地编写所有代码,并在一次移动中将变量分配给“此值或该值”。例如在C#中你可以写:

price = (user entered a price) ? (user entered value) : (default value)
# var = IF   [boolean test]    ? THEN  (x)          ELSE  (y)
如果测试为真,则{p>和?分配(x),如果测试为假,则(y)分配。{/ p>

在Python中,它是写的:

price = (user entered value) if (user entered a price) else (default value)

在PowerShell中,它写道:

# you can't have a ternary operator in PowerShell, because reasons. 

呀。没有很好的短代码模式。

但你可以做的是滥用数组索引(@('x', 'y')[0] is 'x'@('x', 'y')[1] is 'y'和)并编写那个丑陋且令人困惑的代码 - 高尔夫线:

$price = ($defaultValue,$userValue)[[bool]$UserEnteredPrice]

# var    (x,y) is an array         $array[ ] is array indexing
         (0,1) are the array indexes of the two values

                                    [bool]$UserEnteredPrice casts the 'test' part to a True/False value
                                    [True/False] used as indexes into an array indexing makes no sense
                                                  so they implicitly cast to integers, and become 0/1

# so if the test is true, the $UserValue is assigned to $price, and if the test fails, the $DefaultValue is assigned to price.

它的行为就像一个三元运算符,除了它令人困惑和丑陋,在某些情况下,如果你不小心评估两个数组表达式而不管选择哪一个都不会引起你的兴趣(与真实?不同运算符)。

编辑:我应该添加的是我更喜欢的PowerShell表单 - 您可以直接在PowerShell中分配if测试的结果并执行:

$price = if ($userValue) { $userValue } else { $DefaultValue }

# -> 

$prompt = if ($prompt) { $prompt } else { $DefaultValue }

答案 1 :(得分:2)

$prompt投射到[bool]会产生$true$false的值,具体取决于变量是否为空($null或空字符串都变为{{ 1}})或不(非emtpy字符串变为$false)。

[bool]''          → $false
[bool]'something' → $true

在索引运算符中使用该布尔值,然后隐式地将值转换为整数,其中$true变为0并且$false变为1,因此选择数组的第一个或第二个元素。

[int]$false → 0
[int]$true  → 1
($defaultValue,$prompt)[0] → $defaultValue
($defaultValue,$prompt)[1] → $prompt

答案 2 :(得分:0)

补充给出by Ansgar Wiechersby TessellatingHeckler的好答案:

如果 PowerShell拥有ternary conditionalsnull-coalescing的运算符,

# Ternary conditional
# Note: does NOT work in PowerShell as of PSv5.1
$prompt = $prompt ? $prompt : $defaultValue

# Or, more succinctly, with null coalescence:
# Note: does NOT work in PowerShell as of PSv5.1
# (Note: This example assumes that $prompt will be $null in the default
#        case, whereas the code in the question actually assigns the
#        empty string to $prompt if the user just presses Enter.)
$prompt = $prompt ?? $defaultValue

不幸的是,这些富有表现力的构造(仍)不是PowerShell的一部分,而且看起来PowerShell团队已经进入了一段长达十年的失望期,截至撰写本文时已经持续了近十年:

  

在微软,“出货就是选择”。我们对无法在V1.0中发货感到非常失望的事情之一是三元运算符。

来自PowerShell Team blog post日期为2006年12月29日。

同一篇博客文章以函数的形式提供了一些(不完美)模仿这些运算符的权限。

然而,尝试“修复”某种语言总是很棘手,所以我们希望有一天能够正确实施。

以下是博客文章中功能的改编版本以及相关的别名定义,然后使用以下解决方案:

# Ternary conditional - note how the alias must come *first*
# Note: Requires the function and alias defined below.
$prompt = ?: $prompt $prompt $defaultValue

# Or, more succinctly, with null coalescence - note how the alias must come *first*
# Note: Requires the function and alias defined below.
$prompt = ?? $prompt $defaultValue

源代码

请注意,实际功能很短;基于评论的帮助使这个列表变得冗长。

Set-Alias ?: Invoke-Ternary -Option AllScope
<#
.SYNOPSIS
Emulation of a ternary conditional operator.

.DESCRIPTION
An emulation of the still-missing-from-the-PS-language ternary conditional,
such as the C-style <predicate> ? <if-true> : <if-false>

Because a function is used for emulation, however, the function name must
come first in the invocation.

If you define a succinct alias, e.g., set-alias ?: Invoke-Ternary,
concise in-line conditionals become possible.

To specify something other than a literal or a variable reference, pass a
script block for any of the tree operands.
A predicate script block is of necessity always evaluated, but a script block
passed to the true or false branch is only evaluated on demand. 

.EXAMPLE
> Invoke-Ternary { 2 -lt 3 } 'yes' 'no'

Evaluates the predicate script block, which outputs $true, and therefore 
selects and outputs the true-case expression, string 'yes'. 

.EXAMPLE
> Invoke-Ternary $false { $global:foo = 'bar' } { Get-Date }

Outputs the result of executing Get-Date.
Note that the true-case script block is NOT evaluated in this case.

.NOTES
Gratefully adapted from http://blogs.msdn.com/powershell/archive/2006/12/29/dyi-ternary-operator.aspx
#>
function Invoke-Ternary
{
  [CmdletBinding()]
  param($Predicate, $Then, $Otherwise = $null)

  if ($(if ($Predicate -is [scriptblock]) { & $Predicate } else { $Predicate })) {
     if ($Then -is [ScriptBlock]) { & $Then } else { $Then }
  } else {
     if ($Otherwise -is [ScriptBlock]) { & $Otherwise } else { $Otherwise }
  }
}


Set-Alias ?? Invoke-NullCoalescence -Option AllScope
<#
.SYNOPSIS
Emulation of a null-coalescence operator.

.DESCRIPTION
An emulation of a null-coalescence operator such as the following:
<expr> ?? <alternative-expr-if-expr-is-null>

Because a function is used for emulation, however, the function name must
come first in the invocation.

If you define a succinct alias, e.g., set-alias ?? Invoke-NullCoalescence,
concise in-line null-coalescing becomes possible.

To specify something other than a literal or a variable reference, pass a
script block for any of the two operands.
A first-operand script block is of necessity always evaluated, but a
second-operand script block is only evaluated on demand.

Note that only a true $null value in the first operand causes the second 
operand to be returned. 

.EXAMPLE
> Invoke-NullCoalescence $null '(empty)'

Since the first operand is $null, the second operand, string '(empty)', is
output.

.EXAMPLE
> Invoke-NullCoalescence '' { $global:foo = 'bar' }

Outputs the first operand, the empty string, because it is not $null.
Note that the second-operand script block is NOT evaluated in this case.

.NOTES
Gratefully adapted from http://blogs.msdn.com/powershell/archive/2006/12/29/dyi-ternary-operator.aspx
#>
function Invoke-NullCoalescence
{
  [CmdletBinding()]
  param($Value, $Alternative)

  if ($Value -is [scriptblock]) { $Value = & $Value }

  if ($null -ne $Value) {
     $Value
  } else {
     if ($Alternative -is [ScriptBlock]) { & $Alternative } else { $Alternative }
  }
}