PowerShellの三項演算子


214

私が知っていることから、PowerShellにはいわゆる3項演算子の組み込み式がないようです。

たとえば、三項演算子をサポートするC言語では、次のように書くことができます。

<condition> ? <condition-is-true> : <condition-is-false>;

それがPowerShellに実際に存在しない場合、同じ結果を達成するための最良の方法(つまり、読みやすく、保守しやすい)は何でしょうか。


5
見てくださいgithub.com/nightroman/PowerShellTraps/tree/master/Basic/...を。これがあなたが探しているものなら、私はそれを答えにすることができます。
ローマクズミン

2
それはだ、条件オペレータまたは三元場合。これは、「3項演算子」ではありません。これは、3つの引数を取る演算子(任意の演算子)であることを意味します。
Damien_The_Unbeliever 2015

7
@Damien_The_Unbelieverそれは技術的には真実ですが、しばしば三項演算子と呼ばれます。「この演算子は多くの場合、この言語で唯一存在する3項演算子であるため、単に「3項演算子」と呼ばれることもあります。一部の言語では、この演算子は「条件演算子」と呼ばれます
。3

Visual Basicには真の3項演算子はありませんが、IFとIFFは機能的に同等であると見なされます。
Matt

@マット不正解です。IIF この関数は、常に両方のオペランドを評価します。If文はありません-を参照してstackoverflow.com/questions/1220411/...:(新しいVB.NET版は三元表現を追加If (x, y, z)
user2864740

回答:


319
$result = If ($condition) {"true"} Else {"false"}

他のすべては偶発的な複雑さであり、したがって回避する必要があります。

代入だけでなく式で、または式として使用するには$()、それをでラップします。

write-host  $(If ($condition) {"true"} Else {"false"}) 

3
それは等号の右側で機能しますが、三項演算子を期待するほどではありません-これらは失敗します:"a" + If($ condition){"true"} Else {"false"}および"a" + (If($ condition){"true"} Else {"false"}) これは機能します(理由はまだわかりません):"a" + $(If($ condition){"true"} Else {"false "})
ラマース2016年

12
@Lamarth-これは、$()ラッパーがステートメントを式として評価することを強制するため機能し、三項演算子が期待するように、真の値または偽の値のいずれかを返します。これは、PowerShell AFAIKで取得できる最も近いものです。
KeithS 2017年

4
他のいくつかの「非関数」の回答とは異なり、これにより、1つのブランチのみが実行されること保証されます。
user2864740 2018年

63

それをエミュレートするために私が思いついた最も近いPowerShellコンストラクトは次のとおりです。

@({'condition is false'},{'condition is true'})[$condition]

15
({true}, {false})[!$condition](おそらく)少し優れています:a)真と偽の部分の従来の順序。b)$condition0または1、または$ false、$ trueである必要はありません。オペレーター!は必要に応じてそれを変換します。すなわち$condition!42〜$ falseを〜0〜最初の式:42、言うこと、することができます。
ローマクズミン2015

1
まったく同じではありません、@ RomanKuzmin。mjolinorの例は文字列を返します。しかし、式にプラグインされた彼の例の同じ文字列値は、スクリプトブロックを返します。:-(
マイケル・ソレンス

5
つまり{true}{false}とは<true expression><false expression>スクリプトブロックではありません。正確ではありません。
ローマクズミン2015

1
説明をありがとう、@ RomanKuzmin--今私はあなたの提案に価値を見いだしています。
Michael Sorens、2015

12
これは、左と右のオプションの熱心な評価を強制します。これは、適切な3項演算とは大きく異なります。
user2864740 2018年

24

このPowerShellブログ投稿に従って、エイリアスを作成して?:演算子を定義できます。

set-alias ?: Invoke-Ternary -Option AllScope -Description "PSCX filter alias"
filter Invoke-Ternary ([scriptblock]$decider, [scriptblock]$ifTrue, [scriptblock]$ifFalse) 
{
   if (&$decider) { 
      &$ifTrue
   } else { 
      &$ifFalse 
   }
}

次のように使用します。

$total = ($quantity * $price ) * (?:  {$quantity -le 10} {.9} {.75})

「決定」がスクリプトブロックである理由はないようです。?:が評価される場合は、引数が評価される必要があります。スクリプトブロックがなければ、同様の使用法になります。(?: ($quantity -le 10) {.9} {.75})
user2864740 2018年

これはクールな機能ですが、スクリプトを非標準としてレンダリングする可能性があります。たとえば、エイリアス定義も一緒に移植されない限り、スクリプトまたはその一部が移植されない可能性があります。この時点で、基本的に独自のサブ言語を作成しています。これらすべてにより、複雑さが増し、可読性が低下するため、メンテナンスのコストが増加する可能性があります。
Vance McCorkle

1
@VanceMcCorkleソリッドポイント。カスタムの簡潔さは、頻繁に役立つ場合には、そのコストに見合う価値があります。しかし、状況がまれにしか発生しない場合は、「if」式を使用します。
エドワードブレイ


21

私もより良い答えを探しました。エドワードの投稿の解決策は「大丈夫」ですが、このブログ投稿でははるかに自然な解決策を思いつきました

短くて甘い:

# ---------------------------------------------------------------------------
# Name:   Invoke-Assignment
# Alias:  =
# Author: Garrett Serack (@FearTheCowboy)
# Desc:   Enables expressions like the C# operators: 
#         Ternary: 
#             <condition> ? <trueresult> : <falseresult> 
#             e.g. 
#                status = (age > 50) ? "old" : "young";
#         Null-Coalescing 
#             <value> ?? <value-if-value-is-null>
#             e.g.
#                name = GetName() ?? "No Name";
#             
# Ternary Usage:  
#         $status == ($age > 50) ? "old" : "young"
#
# Null Coalescing Usage:
#         $name = (get-name) ? "No Name" 
# ---------------------------------------------------------------------------

# returns the evaluated value of the parameter passed in, 
# executing it, if it is a scriptblock   
function eval($item) {
    if( $item -ne $null ) {
        if( $item -is "ScriptBlock" ) {
            return & $item
        }
        return $item
    }
    return $null
}

# an extended assignment function; implements logic for Ternarys and Null-Coalescing expressions
function Invoke-Assignment {
    if( $args ) {
        # ternary
        if ($p = [array]::IndexOf($args,'?' )+1) {
            if (eval($args[0])) {
                return eval($args[$p])
            } 
            return eval($args[([array]::IndexOf($args,':',$p))+1]) 
        }

        # null-coalescing
        if ($p = ([array]::IndexOf($args,'??',$p)+1)) {
            if ($result = eval($args[0])) {
                return $result
            } 
            return eval($args[$p])
        } 

        # neither ternary or null-coalescing, just a value  
        return eval($args[0])
    }
    return $null
}

# alias the function to the equals sign (which doesn't impede the normal use of = )
set-alias = Invoke-Assignment -Option AllScope -Description "FearTheCowboy's Invoke-Assignment."

これにより、次のようなことを簡単に行うことができます(ブログ投稿のその他の例):

$message == ($age > 50) ? "Old Man" :"Young Dude" 

1
これはかなり素晴らしいです。=エイリアスとして使用するのは好きではありません。これ==は、C#や他の多くの言語で同等性チェックに使用されているためです。C#である程度の経験を持つことは、Powershellerの間でかなり一般的であり、結果として得られるコードは少し混乱します。:おそらくより良いエイリアスになるでしょう。変数の割り当てと組み合わせて=:使用すると、Pascalで使用されている割り当てを思い出すかもしれませんが、VB.NETやC#ですぐに対応する(私が知っている)割り当てはありません。
2017年

これには、任意のエイリアスを設定できます。: または~、何でもあなたのボートを浮かせます。:D set-alias : Invoke-Assignment -Option AllScope -Description "FearTheCowboy's Invoke-Assignment." #三$message =: ($age > 50) ? "Old Man" :"Young Dude" #ヌル合体$message =: $foo ?? "foo was empty"`
ギャレットSerack

私は知っている、そして私はそうしました、私はなぜ別のエイリアスを使用するのかコメントしているだけでした。
2017年

15

代替として、Powershellのswitchステートメントを試してみてください。特に、変数の割り当て-複数行ですが、読みやすいです。

例、

$WinVer = switch ( Test-Path $Env:windir\SysWOW64 ) {
  $true    { "64-bit" }
  $false   { "32-bit" }
}
"This version of Windows is $WinVer"

1
これは少し冗長かもしれませんが、他の多くの回答に比べて大きな利点があります。特に、外部コードや、スクリプトやモジュールにコピー/貼り付けされる関数には依存していません。
bshacklett 2018

9

三項演算子は通常、値を割り当てるときに使用されるため、値を返す必要があります。これが機能する方法です:

$var=@("value if false","value if true")[[byte](condition)]

愚かですが、働いています。また、この構造を使用して、intを別の値にすばやく変換し、配列要素を追加して、0から始まる非負の値を返す式を指定することができます。


6
これは、左と右のオプションの熱心な評価を強制します。これは、適切な3項演算とは大きく異なります。
user2864740 2018年

6

私はこれをすでに何度も使用していて、ここにリストされていないので、自分の作品を追加します。

$var = @{$true="this is true";$false="this is false"}[1 -eq 1]

すべての醜い!

ちょっとソース


4
これは、左と右のオプションの熱心な評価を強制します。これは、適切な3項演算とは大きく異なります。
user2864740 2018年

@ user2864740これは、PSに適切な3項演算がないためです。これは合成バリアントです。
ViggoLundén19年

2
@ViggoLundén適切な三項演算子の欠如は、コメントの正確さを低下させません。この「合成バリアント」の動作は異なります
user2864740

あなたは正しい、そしてあなたのコメントをもう一度読んだ後、私はそれがいかにして真の違いを生むことができるかを理解している。ありがとう。
sodawillow

5

私は最近改善しました(PullRequestを開きます)、PoweShell lib 'Pscx'の三項条件付きおよびnull融合演算子
Plsは私のソリューションを探します。


私のgithubトピックブランチ: UtilityModule_Invoke-Operators

関数:

Invoke-Ternary
Invoke-TernaryAsPipe
Invoke-NullCoalescing
NullCoalescingAsPipe

エイリアス

Set-Alias :?:   Pscx\Invoke-Ternary                     -Description "PSCX alias"
Set-Alias ?:    Pscx\Invoke-TernaryAsPipe               -Description "PSCX alias"
Set-Alias :??   Pscx\Invoke-NullCoalescing              -Description "PSCX alias"
Set-Alias ??    Pscx\Invoke-NullCoalescingAsPipe        -Description "PSCX alias"

使用法

<condition_expression> |?: <true_expression> <false_expression>

<variable_expression> |?? <alternate_expression>

式として渡すことができます:
、$ null、リテラル、変数、「外部」式($ b -eq 4)、またはスクリプトブロック{$ b -eq 4}

変数式の変数が$ nullまたは存在しない場合です。 、代替式は出力として評価されます。


4

PowerShellには現在、ネイティブのインラインIf(または3項If)がありません)がありませんが、カスタムコマンドレットの使用を検討できます。

IIf <condition> <condition-is-true> <condition-is-false>
参照:PowerShellインラインIf(IIf)


リンクされた投稿は、関数の作成方法を示しIIfます。ただし、標準のPowerShell IIf関数/コマンドレットはありません。
user2864740 2018年

1
@ user2864740、それで何?それは質問に対する可能な使用可能な回答としての回答を除外しますか:「同じ結果を達成するための最良の方法(つまり、読みやすく、保守しやすい)は何でしょうか?」それはさておき、リンクはこれと非常によく似たIIfの質問(2014 年9月5日投稿)を参照しています(重複していますか?)。リンクされた質問の残りの回答も付加価値と見なすことができます(そうでない場合、主に重複しているためです)。
iRon 2018年

少しの説明は長い道のりであり、追加の情報がない場合は重複のために閉じられないので、ベアリンクが推奨されないのはこのためです。非常に小さな説明で、このようなベアリンクの回答の質が大幅に向上することを考慮してください。この方法、または他の回答を参照してください).. "しかし、それは私の仕事ではありません 回答は必要に応じて修正/更新/などできます。
user2864740

@ user2864740、フィードバックをありがとう、それに応じて回答を調整しました。
iRon 2018年

1

次に、カスタム関数の代替アプローチを示します。

function Test-TernaryOperatorCondition {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipeline = $true, Mandatory = $true)]
        [bool]$ConditionResult
        ,
        [Parameter(Mandatory = $true, Position = 0)]
        [PSObject]$ValueIfTrue
        ,
        [Parameter(Mandatory = $true, Position = 1)]
        [ValidateSet(':')]
        [char]$Colon
        ,
        [Parameter(Mandatory = $true, Position = 2)]
        [PSObject]$ValueIfFalse
    )
    process {
        if ($ConditionResult) {
            $ValueIfTrue
        }
        else {
            $ValueIfFalse
        }
    }
}
set-alias -Name '???' -Value 'Test-TernaryOperatorCondition'

1 -eq 1 |??? 'match' : 'nomatch'
1 -eq 2 |??? 'match' : 'nomatch'

相違点の説明

  • なぜ1ではなく3つの疑問符があるのですか?
    • ?文字はすでにの別名ですWhere-Object
    • ?? 他の言語ではnullの合体演算子として使用されており、混乱を避けたいと思いました。
  • コマンドの前にパイプが必要なのはなぜですか?
    • これを評価するためにパイプラインを利用しているので、条件を関数にパイプするためにこの文字が必要です
  • 配列を渡すとどうなりますか?
    • 各値の結果を取得します。つまり-2..2 |??? 'match' : 'nomatch'、次のようになりますmatch, match, nomatch, match, match(つまり、ゼロ以外のintはに評価されるためtrue、ゼロはに評価されるためfalse)。
    • それが必要ない場合は、配列をブール値に変換してください。([bool](-2..2)) |??? 'match' : 'nomatch'(あるいは単に:[bool](-2..2) |??? 'match' : 'nomatch'

1

ブール条件に基づいて文字列または数値を代入/返す構文的に簡単な方法を探しているだけの場合は、次のような乗算演算子を使用できます。

"Condition is "+("true"*$condition)+("false"*!$condition)
(12.34*$condition)+(56.78*!$condition)

何かが真の場合にのみ結果に関心がある場合は、単純なスコアリングシステムなど、偽の部分を完全に省略できます(またはその逆)。

$isTall = $true
$isDark = $false
$isHandsome = $true

$score = (2*$isTall)+(4*$isDark)+(10*$isHandsome)
"Score = $score"
# or
# "Score = $((2*$isTall)+(4*$isDark)+(10*$isHandsome))"

ブール値は乗算の先頭の用語であってはならないことに注意してください。つまり、$ condition * "true"などは機能しません。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.