【问题标题】:$prompt = ($defaultValue,$prompt)[[bool]$prompt] - emulating a ternary conditional in PowerShell$prompt = ($defaultValue,$prompt)[[bool]$prompt] - 在 PowerShell 中模拟三元条件
【发布时间】:2017-04-15 00:00:03
【问题描述】:

我正在学习使用 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 数据类型强制为布尔值,但我不明白这第三行代码的作用整个。

【问题讨论】:

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


    【解决方案1】:

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

    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)
    

    如果测试为真,? 分配(x),如果测试为假,则分配(y)

    在 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 }
    

    【讨论】:

    • 感谢您提供详细的答案以及有关其他编程语言的信息。
    【解决方案2】:

    $prompt 转换为[bool] 会产生$true$false 的值,具体取决于变量是否为空($null 或空字符串都变为$false)(非空字符串变为$true)。

    [bool]'' → $false
    [bool]'某事'→$true

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

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

    【讨论】:

      【解决方案3】:

      补充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 中发布感到非常失望的一件事是三元运算符。

      来自 2006 年 12 月 29 日的 PowerShell Team blog post

      同一篇博文以函数的形式提供了权宜之计,它们(不完美地)模拟这些运算符。

      然而,尝试“修复”一门语言总是一件棘手的事情,所以我们希望有一天能得到正确的实施。

      以下是博客文章中函数的改编版本以及相关的别名定义,使用这些函数可以实现以下解决方案:

      # 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 }
        }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-11-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-23
        相关资源
        最近更新 更多