จะทำ one-liner if else statement ได้อย่างไร?


113

ฉันสามารถเขียนคำสั่ง if-else อย่างง่ายพร้อมการกำหนดตัวแปรใน go (golang) เหมือนที่ฉันทำใน php ได้หรือไม่ ตัวอย่างเช่น:

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

ตอนนี้ฉันต้องใช้สิ่งต่อไปนี้:

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

ขออภัยฉันจำชื่อไม่ได้หากคำสั่งควบคุมนี้และฉันไม่พบข้อมูลในไซต์หรือจากการค้นหาโดย Google : /


4
เรียกว่าตัวดำเนินการ ternary ... และไม่ Go ไม่มี
Simon Whitehead

6
ฉันเชื่อว่าคำที่คุณกำลังมองหาคือ "ternary"
BenjaminRH


11
เพื่อชี้แจงว่าตัวดำเนินการ ternary คือตัวดำเนินการใด ๆ ของ arity 3 นั่นคือโอเปอเรเตอร์ใด ๆ ที่ผูก 3 นิพจน์ย่อย C มีตัวดำเนินการดังกล่าวเพียงตัวเดียว นั่นเป็นเหตุผลที่มักเรียกว่าตัวดำเนินการ ternary ชื่อจริงของมันคือ "ตัวดำเนินการตามเงื่อนไข"
thwd

คำตอบ:


143

ตามความคิดเห็นที่กล่าวไป Go ไม่สนับสนุน ternary one liners รูปแบบที่สั้นที่สุดที่ฉันคิดได้คือ:

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

แต่อย่าทำอย่างนั้นมันไม่คุ้มค่าและจะทำให้คนที่อ่านโค้ดของคุณสับสนเท่านั้น


87
@thoroc ฉันอาจหลีกเลี่ยงสิ่งนั้นในชีวิตจริงเนื่องจากไม่ใช่ IMHO ที่ใช้งานง่ายจึงไม่คุ้มค่าที่จะบันทึก 2 บรรทัดเพื่อให้อ่านได้น้อยลง
Not_a_Golfer

14
@thoroc โปรดฟังสิ่งที่ Not_a_Golfer พูด คุณควรพยายามเขียนซอฟต์แวร์ที่ดูแลรักษาได้ไม่ใช่อวดตัวเอง เทคนิคที่เรียบร้อยเป็นระเบียบ แต่ผู้ชายคนต่อไปที่อ่านรหัสของคุณ (รวมถึงคุณในสองสามเดือน / ปี) จะไม่ชื่นชมพวกเขา
kostix

3
ขึ้นอยู่กับความสามารถในการอ่าน ฉันคิดว่านี่เป็นเรื่องที่อ่านได้มากกว่า แต่ก็เหมือนกับสิ่งอื่น ๆ ที่สามารถใช้ในทางที่ผิดได้ หากคุณต้องการประกาศตัวแปรที่ไม่ต้องการนอก if block ฉันคิดว่านี่คือผู้ชนะ
arjabbar

@Not_a_Golfer ไม่เพียง แต่สามารถอ่านได้ด้วยความสมมาตรเท่านั้นความแตกต่างของโครงสร้างกับความตั้งใจยังอยู่ในโค้ดที่คอมไพล์บางครั้งแม้จะมีค่าใช้จ่ายเพิ่มเติมสำหรับการเริ่มต้นที่ซ้ำซ้อน
Wolf

5
โอ้ความสุขดูสิว่า Go ได้รับเท่าไรจากการไม่มีตัวดำเนินการที่เกี่ยวข้อง นอกเหนือจากความคิดเห็นที่อ่านได้แล้วคุณได้เปลี่ยนโครงสร้างที่ประเมินอย่างเฉื่อยชาซึ่งมีเพียงสาขาเดียวเท่านั้นที่จะถูกเรียกใช้ไปยังอันที่หนึ่งซึ่งรันครั้งแรกเสมอและอันที่สองอาจถูกเรียกใช้เพิ่มเติม
itsbruce

32

ฉันมักจะใช้สิ่งต่อไปนี้:

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

พื้นเช่นเดียวกับ @ Not_a_Golfer แต่ใช้อนุมานชนิด


4
ด้วยข้อบกพร่องเดียวกัน: คุณทำความเข้าใจให้ซับซ้อนขึ้นเมื่อใช้วิธีแก้ปัญหาแบบอสมมาตรเพื่อความต้องการสมมาตรที่เห็นได้ชัด
Wolf

2
และข้อบกพร่องเดียวกันกับการเปลี่ยนสิ่งที่ควรจะเป็นโครงสร้างที่ประเมินอย่างเฉื่อยชาซึ่งจะใช้เพียงหนึ่งในสองสาขาในสาขาที่หนึ่งจะได้รับการประเมินเสมอบางครั้งทั้งสองอย่าง (ซึ่งในกรณีนี้การประเมินครั้งแรกซ้ำซ้อน)
itsbruce

1
สิ่งนี้อาจมีประโยชน์มากขึ้นอยู่กับการใช้งาน เช่นlisteningPath := "production.some.com"; if DEBUG { listeningPath := "development.some.com" }ความเร็วเท่ากันกับ ternary สำหรับการผลิตและ imho อ่านได้ค่อนข้างดี
Levite

ฉันมักจะร้องไห้เมื่อวงจร CPU เสียโดยเจตนา
alessiosavi

28

ดังที่คนอื่น ๆ กล่าวถึงGoไม่สนับสนุนหนึ่งสมุทรที่สาม อย่างไรก็ตามฉันเขียนฟังก์ชันยูทิลิตี้ที่สามารถช่วยให้คุณบรรลุสิ่งที่คุณต้องการได้

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

ต่อไปนี้เป็นกรณีทดสอบเพื่อแสดงวิธีการใช้งาน

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

เพื่อความสนุกสนานฉันเขียนฟังก์ชันยูทิลิตี้ที่มีประโยชน์มากขึ้นเช่น:

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

หากคุณต้องการใช้สิ่งเหล่านี้คุณสามารถค้นหาได้ที่นี่https://github.com/shomali11/util


12

ขอขอบคุณที่ชี้ไปยังคำตอบที่ถูกต้อง

ฉันเพิ่งตรวจสอบคำถามที่พบบ่อยของ Golang (duh) และระบุไว้อย่างชัดเจนว่าไม่มีในภาษา:

Go มีตัวดำเนินการ?: หรือไม่?

ไม่มีแบบฟอร์มใน Go คุณสามารถใช้สิ่งต่อไปนี้เพื่อให้ได้ผลลัพธ์เดียวกัน:

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

พบข้อมูลเพิ่มเติมที่อาจเป็นที่สนใจในหัวข้อนี้:


เป็นไปได้ในบรรทัดเดียว: var c int; if a > b { c = a } else { c = b }? แต่ฉันขอแนะนำให้เก็บไว้ใน 5 บรรทัดเพื่อสร้างบล็อกแสงสำหรับการพักผ่อนหย่อนใจของผู้อ่าน)
Wolf

7

วิธีการหนึ่งที่เป็นไปได้ที่จะทำเช่นนี้ในเวลาเพียงหนึ่งเส้นโดยใช้แผนที่ง่ายฉันกำลังตรวจสอบว่าa > bถ้ามันเป็นtrueฉันกำลังกำหนดcที่จะaเป็นอย่างอื่นb

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

อย่างไรก็ตามสิ่งนี้ดูน่าทึ่ง แต่ในบางกรณีอาจไม่ใช่โซลูชันที่สมบูรณ์แบบเนื่องจากลำดับการประเมิน ตัวอย่างเช่นหากฉันกำลังตรวจสอบว่าวัตถุไม่nilได้รับคุณสมบัติบางอย่างออกมาหรือไม่ให้ดูที่ข้อมูลโค้ดต่อไปนี้ซึ่งจะเป็นpanicกรณีของmyObj 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}

เนื่องจากแผนที่จะถูกสร้างและสร้างขึ้นก่อนที่จะประเมินสภาพดังนั้นในกรณีmyObj = nilนี้จะทำให้ตกใจ

อย่าลืมระบุว่าคุณยังสามารถทำเงื่อนไขได้ในบรรทัดเดียวให้ตรวจสอบสิ่งต่อไปนี้:

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

4

ใช้ฟังก์ชัน lambda แทนตัวดำเนินการ ternary

ตัวอย่าง 1

เพื่อให้ int สูงสุด

package main

func main() {

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

ตัวอย่าง 2

สมมติว่าคุณมีmust(err error)ฟังก์ชันนี้เพื่อจัดการข้อผิดพลาดและคุณต้องการใช้เมื่อเงื่อนไขไม่เป็นไปตามเงื่อนไข (เพลิดเพลินได้ที่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)
}

2
แนวทางที่น่าสนใจอาจเป็นประโยชน์ในกรณีที่ซับซ้อนมากขึ้น :)
thoroc

2

มีโครงสร้างที่คล้ายกันมากในภาษา

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

*

กล่าวคือ

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

2

บางครั้งฉันพยายามใช้ฟังก์ชันนิรนามเพื่อให้การกำหนดและการกำหนดเกิดขึ้นในบรรทัดเดียวกัน เช่นด้านล่าง:

a, b = 4, 8

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

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


สิ่งนี้อาจมีประโยชน์ในกรณีที่ซับซ้อนมากขึ้น +1
Tom L

0

คุณสามารถใช้การปิดสำหรับสิ่งนี้:

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() })
}

สิ่งเดียวที่ฉันมีกับไวยากรณ์การปิดใน Go คือไม่มีนามแฝงสำหรับฟังก์ชันการคืนค่าพารามิเตอร์ศูนย์เริ่มต้นเป็นศูนย์ดังนั้นมันจะดีกว่ามาก (ลองนึกดูว่าคุณประกาศตัวอักษรแผนที่อาร์เรย์และสไลซ์ด้วยชื่อประเภทอย่างไร)

หรือแม้แต่เวอร์ชันที่สั้นกว่าตามที่ผู้แสดงความคิดเห็นแนะนำ:

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

คุณยังคงต้องใช้การปิดถ้าคุณต้องการให้พารามิเตอร์กับฟังก์ชัน สิ่งนี้อาจเป็นอุปสรรคในกรณีของการส่งผ่านเมธอดแทนที่จะเป็นเพียงฟังก์ชันที่ฉันคิดโดยที่พารามิเตอร์เป็นโครงสร้างที่เกี่ยวข้องกับเมธอด


เวอร์ชันสั้นกว่าdoif(condition, dothis, dothat)
vellotis

1
ใช่มันจะสั้นกว่านี้โดยส่งผ่านเพียงฟังก์ชั่น หนึ่งต้องมีฟังก์ชันนี้เพียงครั้งเดียวในไลบรารียูทิลิตี้เดียวและคุณสามารถใช้งานได้ทั้งหมดผ่านรหัสของคุณ
Louki Sumirniy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.