Operator ternary di PowerShell

214

Dari apa yang saya tahu, PowerShell tampaknya tidak memiliki ekspresi bawaan untuk apa yang disebut operator ternary .

Misalnya, dalam bahasa C, yang mendukung operator ternary, saya bisa menulis sesuatu seperti:

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

Jika itu tidak benar-benar ada di PowerShell, apa cara terbaik (yaitu mudah dibaca dan dipelihara) untuk mencapai hasil yang sama?

mguassa
sumber
5
Lihatlah github.com/nightroman/PowerShellTraps/tree/master/Basic/… . Jika ini yang Anda cari, saya bisa menjawabnya.
Roman Kuzmin
2
Ini adalah operator bersyarat atau terner jika . Ini bukan "operator ternary" karena semua yang dimaksud adalah operator ( operator apa pun ) yang membutuhkan tiga argumen.
Damien_The_Unbeliever
7
@Damien_The_Unbeliever Secara teknis itu benar, tetapi sering disebut operator ternary. "Karena operator ini sering merupakan satu-satunya operator ternary yang ada dalam bahasa tersebut, kadang-kadang hanya disebut sebagai" operator ternary ". Dalam beberapa bahasa, operator ini disebut sebagai" operator kondisional. " Operasi
ternary
Visual basic tidak memiliki operator ternary sejati tetapi menganggap IF dan IFF secara fungsional sama.
Matt
@ Matt Itu tidak benar. The IIF fungsi akan selalu mengevaluasi kedua operan. The Ifpernyataan tidak akan - melihat stackoverflow.com/questions/1220411/... (versi VB.NET baru menambahkan ekspresi ternary: If (x, y, z))
user2864740

Jawaban:

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

Yang lainnya adalah kompleksitas insidental dan karenanya harus dihindari.

Untuk digunakan dalam atau sebagai ekspresi, bukan hanya tugas, bungkuslah $(), dengan demikian:

write-host  $(If ($condition) {"true"} Else {"false"}) 
fbehrens
sumber
3
Yang bekerja di sisi kanan sama dengan, tetapi tidak cukup seperti yang Anda harapkan operator ternary - ini gagal: "a" + If ($ condition) {"true"} Else {"false"} dan "a" + (If ($ condition) {"true"} Else {"false"}) Ini berfungsi (saya belum yakin mengapa): "a" + $ (If ($ condition) {"true"} Else {"false "})
Lamarth
12
@Lamarth - Itu bekerja karena $()pembungkus memaksa evaluasi pernyataan sebagai ekspresi, sehingga mengembalikan nilai benar dari nilai salah, seperti operator ternary akan diharapkan. Ini adalah yang terdekat dengan PowerShell AFAIK.
KeithS
4
Tidak seperti beberapa jawaban "tidak berfungsi" lainnya, ini juga akan memastikan bahwa hanya satu cabang yang dieksekusi.
user2864740
63

Konstruk PowerShell terdekat yang dapat saya tiru adalah:

@({'condition is false'},{'condition is true'})[$condition]
mjolinor
sumber
15
({true}, {false})[!$condition]sedikit lebih baik (mungkin): a) tatanan tradisional dari bagian yang benar dan yang salah; b) $conditiontidak harus hanya 0 atau 1 atau $ false, $ true. Operator !mengubahnya sesuai kebutuhan. Yaitu $conditionbisa, katakanlah, 42:! 42 ~ $ false ~ 0 ~ ekspresi pertama.
Roman Kuzmin
1
Tidak terlalu identik, @RomanKuzmin. Contoh mjolinor mengembalikan string. Tetapi nilai string yang sama dari contohnya yang dicolokkan ke ekspresi Anda mengembalikan blok skrip. :-(
Michael Sorens
5
Dengan {true}dan {false}Maksudku <true expression>dan <false expression>, tidak blok skrip. Maaf karena tidak akurat.
Roman Kuzmin
1
Terima kasih atas klarifikasi, @ RomanKuzmin - sekarang saya melihat nilainya dalam saran Anda.
Michael Sorens
12
Ini akan memaksa evaluasi yang penuh semangat dari opsi kiri dan kanan: ini jauh berbeda dari operasi ternary yang tepat.
user2864740
24

Per posting blog PowerShell ini , Anda dapat membuat alias untuk mendefinisikan ?:operator:

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

Gunakan seperti ini:

$total = ($quantity * $price ) * (?:  {$quantity -le 10} {.9} {.75})
Edward Brey
sumber
Tampaknya tidak ada alasan bahwa 'penentu' adalah scriptblock. Jika ?:pernah dievaluasi maka akan membutuhkan argumen yang dievaluasi. Tanpa blok skrip itu akan memiliki penggunaan yang serupa, misalnya. (?: ($quantity -le 10) {.9} {.75})
user2864740
Itu adalah fitur keren tetapi dapat membuat skrip Anda sebagai non-standar misalnya skrip Anda atau bagian dari itu mungkin tidak port kecuali definisi alias juga porting dengan itu. Anda pada dasarnya sedang membuat sub-bahasa Anda sendiri pada saat ini. Semua ini dapat menyebabkan peningkatan biaya pemeliharaan karena meningkatnya kompleksitas dan penurunan keterbacaan.
Vance McCorkle
1
@VanceMcCorkle Solid point. Keleness kustom bernilai biaya jika berguna sering. Tetapi jika situasinya jarang terjadi, saya akan tetap dengan ekspresi "jika".
Edward Brey
21

Saya juga mencari jawaban yang lebih baik, dan sementara solusi di pos Edward adalah "ok", saya datang dengan jawaban yang jauh lebih alami. di posting blog ini

Pendek dan manis:

# ---------------------------------------------------------------------------
# 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."

Yang membuatnya mudah untuk melakukan hal-hal seperti (lebih banyak contoh di posting blog):

$message == ($age > 50) ? "Old Man" :"Young Dude" 
Garrett Serack
sumber
1
ini sangat mengagumkan. Saya hanya tidak suka menggunakan =sebagai alias, karena ==digunakan dalam C # dan banyak bahasa lain untuk memeriksa kesetaraan. Memiliki pengalaman dalam C # cukup umum di antara PowerShell dan yang membuat kode yang dihasilkan sedikit membingungkan. :mungkin akan menjadi alias yang lebih baik. Menggunakannya dalam hubungannya dengan tugas variabel =:mungkin mengingatkan seseorang tentang tugas yang digunakan dalam Pascal, tetapi tidak memiliki padanan langsung (yang saya tahu) di VB.NET atau C #.
nohwnd
Anda dapat mengatur ini ke alias apa pun yang Anda inginkan. : atau ~atau apa pun yang mengapung perahu Anda. : D set-alias : Invoke-Assignment -Option AllScope -Description "FearTheCowboy's Invoke-Assignment." # ternary $message =: ($age > 50) ? "Old Man" :"Young Dude" # null $message =: $foo ?? "foo was empty"
coalescing
Saya tahu, dan saya lakukan, saya hanya berkomentar mengapa saya akan menggunakan alias lain.
nohwnd
15

Coba pernyataan beralih powershell sebagai alternatif, terutama untuk penugasan variabel - beberapa baris, tetapi dapat dibaca.

Contoh,

$WinVer = switch ( Test-Path $Env:windir\SysWOW64 ) {
  $true    { "64-bit" }
  $false   { "32-bit" }
}
"This version of Windows is $WinVer"
nudl
sumber
1
Ini mungkin sedikit lebih bertele-tele, tetapi ada manfaat signifikan atas banyak jawaban lainnya. Secara khusus, tidak ada ketergantungan pada kode eksternal, atau pada fungsi yang disalin / disisipkan ke dalam skrip atau modul.
bshacklett
9

Karena operator ternary biasanya digunakan ketika menetapkan nilai, operator harus mengembalikan nilai. Inilah cara yang bisa bekerja:

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

Bodoh, tapi berhasil. Konstruksi ini juga dapat digunakan untuk dengan cepat mengubah int menjadi nilai lain, cukup tambahkan elemen array dan tentukan ekspresi yang mengembalikan nilai non-negatif berbasis 0.

Doa malam
sumber
6
Ini akan memaksa evaluasi yang penuh semangat dari opsi kiri dan kanan: ini jauh berbeda dari operasi ternary yang tepat.
user2864740
6

Karena saya sudah menggunakan ini berkali-kali dan tidak melihatnya tercantum di sini, saya akan menambahkan karya saya:

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

paling jelek dari semuanya!

agak sumber

sodawillow
sumber
4
Ini akan memaksa evaluasi yang penuh semangat dari opsi kiri dan kanan: ini jauh berbeda dari operasi ternary yang tepat.
user2864740
@ user2864740 itu karena tidak ada operasi ternary yang tepat di PS. Ini adalah varian sintetis.
Viggo Lundén
2
@ ViggoLundén Kurangnya operator ternary yang tepat tidak mengurangi kebenaran komentar. "Varian sintetis" ini memiliki perilaku berbeda .
user2864740
Anda benar, dan setelah membaca kembali komentar Anda, saya mengerti bagaimana itu dapat membuat perbedaan nyata. Terima kasih.
sodawillow
5

Saya baru-baru ini meningkatkan (buka PullRequest) operator bersyarat dan null ternary di PoweShell lib 'Pscx'
Pls mencari solusi saya.


Cabang topik github saya: UtilityModule_Invoke-Operators

Fungsi:

Invoke-Ternary
Invoke-TernaryAsPipe
Invoke-NullCoalescing
NullCoalescingAsPipe

Alias

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"

Pemakaian

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

<variable_expression> |?? <alternate_expression>

Sebagai ekspresi, Anda dapat meneruskan:
$ null, literal, variabel, ekspresi 'eksternal' ($ b -eq 4) atau scriptblock {$ b -eq 4}

Jika variabel dalam ekspresi variabel adalah $ nol atau tidak ada , ekspresi alternatif dievaluasi sebagai output.

andiDo
sumber
4

PowerShell saat ini tidak memiliki Inline If (atau ternary If ) asli tetapi Anda dapat mempertimbangkan untuk menggunakan cmdlet khusus:

IIf <condition> <condition-is-true> <condition-is-false>
Lihat: PowerShell Inline If (IIf)

besi
sumber
Pos menunjukkan terkait bagaimana membuat sebuah IIffungsi. Namun, tidak ada IIffungsi PowerShell standar / cmdlet.
user2864740
1
@ user2864740, lalu apa? apakah itu mengecualikan jawaban sebagai jawaban yang mungkin digunakan untuk pertanyaan: " apa yang akan menjadi cara terbaik (yaitu mudah dibaca dan dipelihara) untuk mencapai hasil yang sama?" Tapi selain itu, tautannya merujuk ke pertanyaan IIf (diposting 5 September '14) yang sangat mirip dengan ini (duplikat?). Sisa jawaban dalam pertanyaan terkait juga bisa dilihat sebagai nilai tambah (dan jika tidak, itu terutama karena mereka adalah duplikat).
iRon
Sedikit penjelasan berjalan jauh, dan inilah sebabnya tautan kosong tidak disarankan, karena tidak menutup untuk duplikat jika tidak ada informasi tambahan yang ditambahkan. Pertimbangkan bahwa penjelasan yang sangat kecil akan secara signifikan meningkatkan kualitas jawaban bare link : "Powershell tidak mendukung 'ternary' langsung (^ tetapi lihat jawaban lain). Namun, dimungkinkan untuk menulis fungsi seperti (^ menggunakan metode ini, atau lihat jawaban lain) .. ", tapi itu bukan tugas saya untuk menambahkan. Jawaban dapat diubah / diperbarui / dll sebagaimana berlaku.
user2864740
@ user2864740, terima kasih atas tanggapan Anda, saya telah membuat penyesuaian untuk jawabannya.
iRon
1

Berikut adalah pendekatan fungsi kustom alternatif:

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'

Contoh

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

Perbedaan Dijelaskan

  • Mengapa 3 tanda tanya bukan 1?
    • The ?karakter sudah alias untuk Where-Object.
    • ?? digunakan dalam bahasa lain sebagai operator penggabungan nol, dan saya ingin menghindari kebingungan.
  • Mengapa kita membutuhkan pipa sebelum perintah?
    • Karena saya menggunakan pipeline untuk mengevaluasi ini, kita masih memerlukan karakter ini untuk menyalurkan kondisi ke fungsi kita
  • Apa yang terjadi jika saya memasukkan array?
    • Kami mendapatkan hasil untuk setiap nilai; yaitu -2..2 |??? 'match' : 'nomatch'memberi: match, match, nomatch, match, match(yaitu karena tidak ada int nol mengevaluasi true; sedangkan nol mengevaluasi sampai false).
    • Jika Anda tidak menginginkannya, konversikan array menjadi bool; ([bool](-2..2)) |??? 'match' : 'nomatch'(atau hanya: [bool](-2..2) |??? 'match' : 'nomatch')
JohnLBevan
sumber
1

Jika Anda hanya mencari cara sederhana dan sintaksis untuk menetapkan / mengembalikan string atau numerik berdasarkan kondisi boolean, Anda dapat menggunakan operator perkalian seperti ini:

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

Jika Anda hanya tertarik pada hasil ketika sesuatu itu benar, Anda bisa menghilangkan bagian yang salah sepenuhnya (atau sebaliknya), misalnya sistem penilaian sederhana:

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

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

Perhatikan bahwa nilai boolean tidak boleh menjadi istilah terkemuka dalam perkalian, yaitu $ condition * "true" dll. Tidak akan berfungsi.

nimbell
sumber
1

Pada PowerShell versi 7, operator ternary dibangun ke dalam PowerShell.

1 -gt 2 ? "Yes" : "No"
Trevor Sullivan
sumber