W C / C ++ (i wielu językach tej rodziny) wspólny idiom do deklarowania i inicjalizacji zmiennej w zależności od warunku używa potrójnego operatora warunkowego:
int index = val > 0 ? val : -val
Go nie ma operatora warunkowego. Jaki jest najbardziej idiomatyczny sposób implementacji tego samego fragmentu kodu jak powyżej? Doszedłem do następującego rozwiązania, ale wydaje się dość gadatliwe
var index int
if val > 0 {
index = val
} else {
index = -val
}
Czy jest coś lepszego?
int index = -val + 2 * val * (val > 0);
Odpowiedzi:
Jak wskazano (i miejmy nadzieję, że nic dziwnego), używanie
if+else
jest w rzeczywistości idiomatycznym sposobem wykonywania warunkowych w Go.Oprócz pełnego
var+if+else
bloku kodu pisownia ta jest również często używana:a jeśli masz blok kodu, który jest wystarczająco powtarzalny, taki jak jego odpowiednik
int value = a <= b ? a : b
, możesz utworzyć funkcję, aby go zatrzymać:Kompilator wprowadzi takie proste funkcje, dzięki czemu będzie szybki, wyraźniejszy i krótszy.
źródło
c := (map[bool]int{true: a, false: a - 1})[a > b]
jest przykładem zaciemnienia IMHO, nawet jeśli działa.if/else
jest idiomatyczne podejście to może Golang mogłaby rozważyć pozwalającif/else
klauzule zwrócić wartość:x = if a {1} else {0}
. Go bynajmniej nie byłby jedynym językiem, który działałby w ten sposób. Przykładem głównego nurtu jest Scala. Zobacz: alvinalexander.com/scala/scala-ternary-operator-syntaxNo Go nie posiada potrójny operator, używając if / else składnia jest idiomatyczne sposób.
źródło
if-else
bloku? A kto mówi, żeif-else
nie jest wykorzystywany w podobny sposób? Nie atakuję cię, po prostu czuję, że wymówka projektantów nie jest wystarczającaZałóżmy, że masz następujące wyrażenie trójkowe (w C):
Idiomatycznym podejściem w Go byłoby po prostu użycie
if
bloku:Może to jednak nie spełniać Twoich wymagań. W moim przypadku potrzebowałem wyrażenia wbudowanego dla szablonu generowania kodu.
Użyłem natychmiast anonimowej funkcji:
Zapewnia to, że obie gałęzie również nie są oceniane.
źródło
expr1 ? expr2 : expr3
. Jeśliexpr1
ocenia totrue
,expr2
jest oceniane i jest wynikiem wyrażenia. W przeciwnym razieexpr3
jest oceniany i podawany jako wynik. Pochodzi z sekcji 2.11 ANSI C Programming Language autorstwa K&R. Rozwiązanie My Go zachowuje tę specyficzną semantykę. @Wolf Czy możesz wyjaśnić, co sugerujesz?Trójskładnik mapy jest łatwy do odczytania bez nawiasów:
źródło
simple and clear code is better than creative code.
To nie osiągnie lepszych wyników, jeśli / else i wymaga obsady, ale działa. FYI:
BenchmarkAbsTernary-8 100000000 18,8 ns / op
BenchmarkAbsIfElse-8 2000000000 0,27 ns / op
źródło
Jeśli wszystkie twoje oddziały powodują skutki uboczne lub są drogie obliczeniowo, następujące byłoby refaktoryzacja semantycznie zachowująca :
bez narzutów (wstawiane) i, co najważniejsze, bez zaśmiecania przestrzeni nazw funkcjami pomocniczymi, które są używane tylko raz (co utrudnia czytelność i konserwację). Przykład na żywo
Uwaga, jeśli naiwnie zastosujesz podejście Gustavo :
dostaniesz program o innym zachowaniu ; na wypadek
val <= 0
, gdyby program wypisał wartość dodatnią, a nie powinien! (Analogicznie, jeśli odwrócisz gałęzie, wprowadzisz koszty ogólne, niepotrzebnie wywołując funkcję slow).źródło
abs
funkcję w oryginalnym kodzie (cóż, chciałbym zmienić<=
na<
). W twoim przykładzie widzę inicjalizację, która w niektórych przypadkach jest zbędna i może być ekspansywna. Czy możesz wyjaśnić: wyjaśnić swój pomysł jeszcze bardziej?printPositiveAndReturn
jest wywoływana tylko dla liczb dodatnich. I odwrotnie, zawsze wykonanie jednej gałęzi, a następnie „ustalenie” wartości przy wykonaniu innej gałęzi nie cofnie skutków ubocznych pierwszej gałęzi .Przedmowa: Bez kłócenia się, że
if else
tak należy, nadal możemy bawić się i czerpać przyjemność z konstrukcji opartych na języku.Poniższy
If
konstrukt jest dostępny w mojejgithub.com/icza/gox
bibliotece z wieloma innymi metodami, które sąbuiltinx.If
typem.Go pozwala na dołączanie metod do dowolnych typów zdefiniowanych przez użytkownika , w tym typów pierwotnych, takich jak
bool
. Możemy stworzyć typ niestandardowy mającybool
jako jego typ podstawowy , a następnie przy pomocy prostej konwersji typu pod warunkiem mamy dostęp do jego metod. Metody, które odbierają i wybierają spośród argumentów.Coś takiego:
Jak możemy tego użyć?
Na przykład trójka robi
max()
:Trójka robi
abs()
:Wygląda to fajnie, jest proste, eleganckie i wydajne (nadaje się również do wstawiania ).
Jeden minus w porównaniu do „prawdziwego” operatora trójskładnikowego: zawsze ocenia wszystkie operandy.
Aby uzyskać odroczoną i tylko w razie potrzeby ocenę, jedyną opcją jest użycie funkcji ( deklarowanych funkcji lub metod lub literałów funkcji ), które są wywoływane tylko wtedy, gdy jest to konieczne:
Korzystanie z niego: Załóżmy, że mamy te funkcje do obliczania
a
ib
:Następnie:
Na przykład warunkiem jest bieżący rok> 2020:
Jeśli chcemy użyć literałów funkcyjnych:
Uwaga końcowa: jeśli miałbyś funkcje z różnymi podpisami, nie możesz ich tutaj użyć. W takim przypadku możesz użyć literału funkcji z pasującym podpisem, aby nadal mieć zastosowanie.
Na przykład jeśli
calca()
icalcb()
miałby również parametry (oprócz wartości zwracanej):Oto jak możesz ich użyć:
Wypróbuj te przykłady na Go Playground .
źródło
Odpowiedź Eolda jest interesująca i kreatywna, a może nawet sprytna.
Jednak zamiast tego zaleca się:
Tak, oba kompilują się w zasadzie do tego samego zestawu, jednak ten kod jest znacznie bardziej czytelny niż wywoływanie funkcji anonimowej tylko po to, aby zwrócić wartość, która mogła zostać zapisana w zmiennej.
Zasadniczo prosty i przejrzysty kod jest lepszy niż kod kreatywny.
Ponadto dowolny kod korzystający z literału mapy nie jest dobrym pomysłem, ponieważ mapy w Go nie są wcale lekkie. Od wersji 1.3, losowa kolejność iteracji dla małych map jest gwarantowana, a aby to wymusić, stała się nieco mniej wydajna pod względem pamięci dla małych map.
W rezultacie tworzenie i usuwanie wielu małych map zajmuje zarówno dużo miejsca, jak i czasu. Miałem kawałek kodu, który korzystał z małej mapy (prawdopodobnie dwa lub trzy klucze, ale częstym przypadkiem użycia był tylko jeden wpis), ale kod był bardzo powolny. Mówimy o co najmniej 3 rzędy wielkości wolniej niż ten sam kod przepisany na klucz z podwójnym wycinkiem [indeks] => mapa danych [indeks]. I prawdopodobnie było więcej. Ponieważ niektóre operacje, które wcześniej trwały kilka minut, rozpoczęły się w milisekundach. \
źródło
simple and clear code is better than creative code
- to bardzo mi się podoba, ale w ostatniej części jestem trochę zdezorientowanydog slow
, może to też może być mylące dla innych?m := map[string]interface{} { a: 42, b: "stuff" }
, a następnie w innej funkcji, iterując po niej:for key, val := range m { code here }
Po przejściu na system dwóch plasterkówkeys = []string{ "a", "b" }, data = []interface{}{ 42, "stuff" }
:, a następnie iteruj jakfor i, key := range keys { val := data[i] ; code here }
rzeczy przyspieszyły 1000 razy.One-linery, choć odrzucane przez twórców, mają swoje miejsce.
Ten rozwiązuje leniwy problem oceny, umożliwiając opcjonalnie przekazanie funkcji do oceny w razie potrzeby:
Wynik
interface{}
aby spełnić operację rzutowania wewnętrznego.c
.Autonomiczne rozwiązanie tutaj jest również miłe, ale może być mniej jasne dla niektórych zastosowań.
źródło