Como fazer uma declaração if else de uma linha?

104

Posso escrever uma instrução if-else simples com atribuição de variável em go (golang) como faria em php? Por exemplo:

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

Atualmente tenho que usar o seguinte:

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

Desculpe, não consigo lembrar o nome desta instrução de controle e não consegui encontrar a informação no site ou através de pesquisa do google. : /

toroc
fonte
4
É chamado de operador ternário ... e não, Go não tem um.
Simon Whitehead,
6
Eu acredito que a palavra que você está procurando é "ternário"
BenjaminRH
10
Só para esclarecer, um operador ternário é qualquer operador de aridade 3, ou seja, qualquer operador que vincule 3 subexpressões. Acontece que C tem apenas um desses operadores. É por isso que geralmente é chamado de operador ternário. Seu nome real é "operador condicional", no entanto.
dia

Respostas:

132

Como os comentários mencionados, Go não oferece suporte a um liners ternário. A forma mais curta que consigo pensar é esta:

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

Mas, por favor, não faça isso, não vale a pena e só vai confundir as pessoas que leem seu código.

Not_a_Golfer
fonte
84
@thoroc Eu provavelmente evitaria isso na vida real, já que é IMHO não intuitivo, não vale a pena economizar 2 linhas para menos legibilidade.
Not_a_Golfer
14
@thoroc, por favor ouça o que Not_a_Golfer disse. Você deve se esforçar para escrever software sustentável, não se exibir. Truques legais são legais, mas o próximo cara lendo seu código (incluindo você em alguns meses / anos) não os apreciará.
kostix
3
Depende, tanto quanto legibilidade. Acho que é mais legível, mas como qualquer outra coisa, pode ser abusado. Se você precisar declarar uma variável que não é necessária fora do bloco if, então acho que é um vencedor.
arjabbar
@Not_a_Golfer não é apenas legibilidade por simetria, o contraste estrutural com a intenção também está no código compilado, às vezes até com custo extra para uma inicialização redundante.
Wolf
5
Oh, alegria, veja o quanto Go ganhou por não ter uma operadora ternária. Além dos comentários de legibilidade feitos, você alterou uma construção avaliada vagarosamente onde apenas um branch é executado para um onde o primeiro é sempre executado e o segundo pode ser executado adicionalmente.
itsbruce de
29

Costumo usar o seguinte:

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

basicamente o mesmo que @Not_a_Golfer, mas usando inferência de tipo .

xoyuz
fonte
4
Com a mesma deficiência: você complica o entendimento ao usar uma solução assimétrica para um requisito obviamente simétrico.
Wolf
2
E a mesma deficiência de transformar o que deveria ser uma construção avaliada vagarosamente, onde apenas um dos dois ramos será usado em um onde um será sempre avaliado, às vezes ambos (neste caso, a primeira avaliação foi redundante)
bruce
1
Dependendo do caso de uso, isso ainda pode ser muito útil. Por exemplo, a listeningPath := "production.some.com"; if DEBUG { listeningPath := "development.some.com" }mesma velocidade que o ternário para produção e boa legibilidade.
Levite
Eu geralmente choro quando desperdiço ciclo de CPU intencionalmente.
alessiosavi
28

Como os outros mencionados, Gonão oferece suporte a one-liners ternários. No entanto, escrevi uma função de utilidade que pode ajudá-lo a conseguir o que deseja.

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

Aqui estão alguns casos de teste para mostrar como você pode usá-lo

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

Para me divertir, escrevi funções utilitárias mais úteis, como:

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

Se quiser usar algum deles, você pode encontrá-los aqui https://github.com/shomali11/util

Raed Shomali
fonte
12

Obrigado por apontar para a resposta correta.

Acabei de verificar o FAQ Golang (duh) e afirma claramente, isso não está disponível no idioma:

O Go tem a operadora?:?

Não existe forma ternária em Go. Você pode usar o seguinte para obter o mesmo resultado:

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

informações adicionais encontradas que podem ser de interesse sobre o assunto:

toroc
fonte
também é possível em uma linha var c int; if a > b { c = a } else { c = b }:? Mas eu sugiro mantê-lo em 5 linhas para formar um bloco de luz para a recreação do leitor;)
Wolf
7

Uma forma possível de fazer isso em apenas uma linha, usando um mapa, simples Estou verificando se a > bse é trueque eu estou atribuindo cao acontráriob

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

No entanto, isso parece incrível, mas em alguns casos pode NÃO ser a solução perfeita por causa da ordem de avaliação. Por exemplo, se estou verificando se um objeto não nilobteve alguma propriedade dele, observe o seguinte trecho de código que, panicno caso demyObj 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}

Porque o mapa será criado e construído primeiro, antes de avaliar a condição, então, nesse caso myObj = nil, simplesmente entrarei em pânico.

Sem esquecer de mencionar que você ainda pode fazer as condições em apenas uma linha simples, verifique o seguinte:

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

Use a função lambda em vez do operador ternário

Exemplo 1

para dar o máximo int

package main

func main() {

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

Exemplo 2

Suponha que você tenha essa must(err error)função para tratar erros e queira usá-la quando uma condição não for atendida. (desfrute em 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
fonte
2
abordagem interessante, pode ser útil em casos mais complexos. :)
thoroc
1

Às vezes, tento usar a função anônima para conseguir definir e atribuir acontecem na mesma linha. como abaixo:

a, b = 4, 8

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

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

Ron
fonte
0

Uma construção muito semelhante está disponível no idioma

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

*

ie

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

Você pode usar um fecho para isso:

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

A única queixa que tenho com a sintaxe de fechamento em Go é que não há alias para a função de retorno zero do parâmetro zero padrão, então seria muito mais agradável (pense em como você declara literais de mapa, matriz e fatia com apenas um nome de tipo).

Ou mesmo a versão mais curta, como um comentador acabou de sugerir:

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

Você ainda precisaria usar um encerramento se precisasse fornecer parâmetros para as funções. Isso poderia ser evitado no caso de passar métodos em vez de apenas funções, eu acho, onde os parâmetros são a estrutura associada aos métodos.

Louki Sumirniy
fonte
Versão mais curtadoif(condition, dothis, dothat)
vellotis
1
Sim, isso seria ainda mais curto, passando apenas as funções. É necessário ter essa função apenas uma vez em uma biblioteca de utilitários e você pode usá-la por meio de seu código.
Louki Sumirniy