Bagaimana melakukan pernyataan one-liner if else?

104

Dapatkah saya menulis pernyataan if-else sederhana dengan tugas variabel di go (golang) seperti yang akan saya lakukan di php? Sebagai contoh:

$var = ( $a > $b )? $a: $b;

Saat ini saya harus menggunakan yang berikut ini:

var c int
if a > b {
    c = a
} else {
    c = b
}

Maaf, saya tidak dapat mengingat nama jika pernyataan kontrol ini dan saya tidak dapat menemukan info di situs atau melalui pencarian google. : /

thoroc.dll
sumber
4
Ini disebut operator terner ... dan tidak, Go tidak memilikinya.
Simon Whitehead
6
Saya yakin kata yang Anda cari adalah "terner"
BenjaminRH
10
Untuk memperjelas, operator terner adalah setiap operator arity 3, yaitu setiap operator yang mengikat 3 sub-ekspresi. C kebetulan hanya memiliki satu operator seperti itu. Karena itulah biasanya disebut operator terner. Nama aslinya adalah "operator bersyarat".
thwd

Jawaban:

132

Seperti yang disebutkan di komentar, Go tidak mendukung terner one liners. Bentuk terpendek yang dapat saya pikirkan adalah ini:

var c int
if c = b; a > b {
    c = a
}

Tapi tolong jangan lakukan itu, itu tidak sepadan dan hanya akan membingungkan orang yang membaca kode Anda.

Not_a_Golfer
sumber
84
@thoroc Saya sebenarnya mungkin menghindari itu dalam kehidupan nyata, karena ini adalah IMHO non intuitif, tidak ada gunanya menyimpan 2 baris agar kurang mudah dibaca.
Not_a_Golfer
14
@thoroc, tolong dengarkan apa yang dikatakan Not_a_Golfer. Anda harus berusaha keras untuk menulis perangkat lunak yang dapat dipelihara, bukan memamerkan diri Anda. Trik yang rapi itu rapi tetapi orang berikutnya yang membaca kode Anda (termasuk Anda dalam beberapa bulan / tahun) tidak akan menyukainya.
kostix
3
Itu tergantung, sejauh keterbacaan. Saya pikir ini lebih mudah dibaca, tetapi seperti yang lainnya, ini dapat disalahgunakan. Jika Anda perlu mendeklarasikan variabel yang tidak diperlukan di luar blok if, maka saya pikir ini adalah pemenangnya.
arjabbar
@Not_a_Golfer tidak hanya dapat dibaca oleh simetri, kontras struktural dengan maksud juga ada dalam kode yang dikompilasi, terkadang bahkan dengan biaya tambahan untuk inisialisasi yang berlebihan.
Serigala
5
Oh, kegembiraan, lihat betapa banyak yang diperoleh Go dengan tidak memiliki operator terner. Selain dari komentar keterbacaan yang dibuat, Anda telah mengubah konstruksi dievaluasi secara malas di mana hanya satu cabang yang pernah dijalankan ke cabang di mana cabang pertama selalu dijalankan dan yang kedua dapat dijalankan sebagai tambahan.
itsbruce
29

Saya sering menggunakan yang berikut ini:

c := b
if a > b {
    c = a
}

pada dasarnya sama dengan @ Not_a_Golfer tetapi menggunakan inferensi tipe .

xoyuz
sumber
4
Dengan kekurangan yang sama: Anda memperumit pemahaman saat menggunakan solusi asimetris untuk kebutuhan yang jelas-jelas simetris.
Serigala
2
Dan kekurangan yang sama untuk mengubah apa yang seharusnya menjadi konstruksi yang dievaluasi dengan malas di mana hanya satu dari dua cabang yang akan pernah digunakan menjadi satu di mana satu akan selalu dievaluasi, terkadang keduanya (dalam hal ini evaluasi pertama berlebihan)
itsbruce
1
Bergantung pada kasus penggunaan, ini mungkin masih sangat berguna. Misalnya listeningPath := "production.some.com"; if DEBUG { listeningPath := "development.some.com" }kecepatan yang sama seperti terner untuk produksi, dan keterbacaan yang cukup baik.
Lewi
Saya biasanya menangis saat sengaja membuang siklus CPU.
alessiosavi
28

Seperti yang disebutkan orang lain, Gotidak mendukung terner satu baris. Namun, saya menulis fungsi utilitas yang dapat membantu Anda mencapai apa yang Anda inginkan.

// IfThenElse evaluates a condition, if true returns the first parameter otherwise the second
func IfThenElse(condition bool, a interface{}, b interface{}) interface{} {
    if condition {
        return a
    }
    return b
}

Berikut beberapa kasus uji untuk menunjukkan bagaimana Anda dapat menggunakannya

func TestIfThenElse(t *testing.T) {
    assert.Equal(t, IfThenElse(1 == 1, "Yes", false), "Yes")
    assert.Equal(t, IfThenElse(1 != 1, nil, 1), 1)
    assert.Equal(t, IfThenElse(1 < 2, nil, "No"), nil)
}

Untuk bersenang-senang, saya menulis fungsi utilitas yang lebih berguna seperti:

IfThen(1 == 1, "Yes") // "Yes"
IfThen(1 != 1, "Woo") // nil
IfThen(1 < 2, "Less") // "Less"

IfThenElse(1 == 1, "Yes", false) // "Yes"
IfThenElse(1 != 1, nil, 1)       // 1
IfThenElse(1 < 2, nil, "No")     // nil

DefaultIfNil(nil, nil)  // nil
DefaultIfNil(nil, "")   // ""
DefaultIfNil("A", "B")  // "A"
DefaultIfNil(true, "B") // true
DefaultIfNil(1, false)  // 1

FirstNonNil(nil, nil)                // nil
FirstNonNil(nil, "")                 // ""
FirstNonNil("A", "B")                // "A"
FirstNonNil(true, "B")               // true
FirstNonNil(1, false)                // 1
FirstNonNil(nil, nil, nil, 10)       // 10
FirstNonNil(nil, nil, nil, nil, nil) // nil
FirstNonNil()                        // nil

Jika Anda ingin menggunakan semua ini, Anda dapat menemukannya di sini https://github.com/shomali11/util

Raed Shomali
sumber
12

Terima kasih telah menunjuk ke jawaban yang benar.

Saya baru saja memeriksa FAQ Golang (ya) dan dengan jelas menyatakan, ini tidak tersedia dalam bahasa:

Apakah Go memiliki operator?:?

Tidak ada bentuk terner di Go. Anda dapat menggunakan yang berikut ini untuk mendapatkan hasil yang sama:

if expr {
    n = trueVal
} else {
    n = falseVal
}

info tambahan ditemukan yang mungkin menarik tentang subjek:

thoroc.dll
sumber
itu juga mungkin dalam satu baris var c int; if a > b { c = a } else { c = b }:? Tapi saya sarankan menyimpannya dalam 5 baris untuk membentuk blok cahaya untuk rekreasi pembaca;)
Wolf
7

Salah satu cara yang mungkin untuk melakukan hal ini hanya dalam satu baris dengan menggunakan peta, sederhana saya memeriksa apakah a > bjika truesaya menugaskan cuntuk asebaliknyab

c := map[bool]int{true: a, false: b}[a > b]

Namun, ini tampak luar biasa tetapi dalam beberapa kasus mungkin BUKAN menjadi solusi yang tepat karena urutan evaluasi. Misalnya, jika saya memeriksa apakah suatu objek tidak nilmendapatkan beberapa properti darinya, lihat cuplikan kode berikut yang akan panicterjadimyObj equals nil

type MyStruct struct {
   field1 string
   field2 string 
}

var myObj *MyStruct
myObj = nil 

myField := map[bool]string{true: myObj.field1, false: "empty!"}[myObj != nil}

Karena peta akan dibuat dan dibangun terlebih dahulu sebelum mengevaluasi kondisinya begitu jika terjadi myObj = nil ini akan panik.

Selain itu, Anda tetap dapat melakukan ketentuan hanya dalam satu baris sederhana, periksa berikut ini:

var c int
...
if a > b { c = a } else { c = b}
Muhammad Soliman
sumber
4

Gunakan fungsi lambda sebagai ganti operator terner

Contoh 1

untuk memberikan int maks

package main

func main() {

    println( func(a,b int) int {if a>b {return a} else {return b} }(1,2) )
}

Contoh 2

Misalkan Anda memiliki must(err error)fungsi ini untuk menangani kesalahan dan Anda ingin menggunakannya ketika kondisi tidak terpenuhi. (nikmati di https://play.golang.com/p/COXyo0qIslP )

package main

import (
    "errors"
    "log"
    "os"
)

// must is a little helper to handle errors. If passed error != nil, it simply panics.
func must(err error) {
    if err != nil {
        log.Println(err)
        panic(err)
    }
}

func main() {

    tmpDir := os.TempDir()
    // Make sure os.TempDir didn't return empty string
    // reusing my favourite `must` helper
    // Isn't that kinda creepy now though?
    must(func() error {
        var err error
        if len(tmpDir) > 0 {
            err = nil
        } else {
            err = errors.New("os.TempDir is empty")
        }
        return err
    }()) // Don't forget that empty parentheses to invoke the lambda.
    println("We happy with", tmpDir)
}
Alice Vixie
sumber
2
pendekatan yang menarik, Ini mungkin berguna dalam kasus yang lebih kompleks. :)
thoroc
1

Terkadang, saya mencoba menggunakan fungsi anonim untuk mencapai pendefinisian dan penetapan terjadi pada baris yang sama. seperti dibawah ini:

a, b = 4, 8

c := func() int {
    if a >b {
      return a
    } 
    return b
  } ()

https://play.golang.org/p/rMjqytMYeQ0

Ron
sumber
0

Konstruksi yang sangat mirip tersedia dalam bahasa tersebut

**if <statement>; <evaluation> {
   [statements ...]
} else {
   [statements ...]
}*

*

yaitu

if path,err := os.Executable(); err != nil {
   log.Println(err)
} else {
   log.Println(path)
}
pengguna2680100
sumber
0

Anda dapat menggunakan closure untuk ini:

func doif(b bool, f1, f2 func()) {
    switch{
    case b:
        f1()
    case !b:   
        f2()
    }
}

func dothis() { fmt.Println("Condition is true") }

func dothat() { fmt.Println("Condition is false") }

func main () {
    condition := true
    doif(condition, func() { dothis() }, func() { dothat() })
}

Satu-satunya keluhan yang saya miliki dengan sintaks penutupan di Go adalah tidak ada alias untuk fungsi nol parameter nol default kembali, maka itu akan jauh lebih baik (pikirkan seperti bagaimana Anda mendeklarasikan peta, array dan irisan literal hanya dengan nama tipe).

Atau bahkan versi yang lebih pendek, seperti yang baru saja disarankan oleh pemberi komentar:

func doif(b bool, f1, f2 func()) {
    switch{
    case b:
        f1()
    case !b:   
        f2()
    }
}

func dothis() { fmt.Println("Condition is true") }

func dothat() { fmt.Println("Condition is false") }

func main () {
    condition := true
    doif(condition, dothis, dothat)
}

Anda masih perlu menggunakan closure jika Anda perlu memberikan parameter pada fungsi. Ini bisa dihindari dalam kasus metode yang lewat daripada hanya fungsi yang saya pikir, di mana parameter adalah struct yang terkait dengan metode.

Louki Sumirniy
sumber
Versi lebih pendekdoif(condition, dothis, dothat)
vellotis
1
Ya, itu akan lebih pendek lagi, hanya melewatkan fungsinya. Anda hanya perlu memiliki fungsi ini sekali dalam satu pustaka utilitas, dan Anda dapat menggunakan semuanya melalui kode Anda.
Louki Sumirniy