Dalam C / C ++ (dan banyak bahasa dari keluarga itu), idiom umum untuk menyatakan dan menginisialisasi variabel tergantung pada suatu kondisi menggunakan operator kondisional ternary:
int index = val > 0 ? val : -val
Go tidak memiliki operator kondisional. Apa cara paling idiomatis untuk mengimplementasikan kode yang sama seperti di atas? Saya datang ke solusi berikut, tetapi sepertinya cukup bertele-tele
var index int
if val > 0 {
index = val
} else {
index = -val
}
Apakah ada sesuatu yang lebih baik?
int index = -val + 2 * val * (val > 0);
Jawaban:
Seperti yang ditunjukkan (dan mudah-mudahan tidak mengejutkan), menggunakan
if+else
memang cara idiomatis untuk melakukan persyaratan di Go.Selain
var+if+else
blok kode yang penuh, ejaan ini juga sering digunakan:dan jika Anda memiliki blok kode yang cukup berulang, seperti yang setara
int value = a <= b ? a : b
, Anda dapat membuat fungsi untuk menahannya:Kompiler akan menampilkan fungsi-fungsi sederhana seperti itu, sehingga cepat, lebih jelas, dan lebih pendek.
sumber
c := (map[bool]int{true: a, false: a - 1})[a > b]
adalah contoh dari IMHO kebingungan, bahkan jika itu berhasil.if/else
adalah pendekatan idiomatik maka mungkin Golang bisa mempertimbangkan membiarkanif/else
klausa mengembalikan nilai:x = if a {1} else {0}
. Go tidak akan berarti satu-satunya bahasa yang bekerja dengan cara ini. Contoh utama adalah Scala. Lihat: alvinalexander.com/scala/scala-ternary-operator-syntaxNo Go tidak memiliki operator ternary, menggunakan sintaks if / else adalah cara idiomatis.
sumber
if-else
blok penuh? Dan siapa bilangif-else
tidak disalahgunakan dengan cara yang sama? Saya tidak menyerang Anda, saya hanya merasa bahwa alasan oleh para desainer tidak cukup validMisalkan Anda memiliki ekspresi ternary berikut (dalam C):
Pendekatan idiomatis di Go adalah dengan menggunakan
if
blok:Namun, itu mungkin tidak sesuai dengan kebutuhan Anda. Dalam kasus saya, saya membutuhkan ekspresi sebaris untuk templat pembuatan kode.
Saya menggunakan fungsi anonim yang langsung dievaluasi:
Ini memastikan bahwa kedua cabang tidak dievaluasi juga.
sumber
expr1 ? expr2 : expr3
. Jikaexpr1
dievaluasi ketrue
,expr2
dievaluasi dan merupakan hasil dari ekspresi. Kalau tidak,expr3
dievaluasi dan diberikan hasilnya. Ini dari Bahasa Pemrograman ANSI C bagian 2.11 oleh K&R. Solusi My Go mempertahankan semantik khusus ini. @ Serigala Bisakah Anda mengklarifikasi apa yang Anda sarankan?Peta ternary mudah dibaca tanpa tanda kurung:
sumber
simple and clear code is better than creative code.
Ini tidak akan mengungguli jika / else dan membutuhkan pemain tetapi berfungsi. FYI:
BenchmarkAbsTernary-8 100000000 18,8 ns / op
BenchmarkAbsIfElse-8 2000000000 0,27 ns / op
sumber
Jika semua cabang Anda membuat efek samping atau mahal secara komputasi , berikut ini akan menjadi refactoring yang melestarikan secara semantik :
dengan biasanya tidak ada overhead (sebaris) dan, yang paling penting, tanpa mengacaukan namespace Anda dengan fungsi pembantu yang hanya digunakan sekali (yang menghambat keterbacaan dan pemeliharaan). Contoh Langsung
Perhatikan jika Anda secara naif menerapkan pendekatan Gustavo :
Anda akan mendapatkan program dengan perilaku yang berbeda ; dalam hal
val <= 0
program akan mencetak nilai non-positif sedangkan seharusnya tidak! (Secara analogi, jika Anda membalikkan cabang, Anda akan memperkenalkan overhead dengan memanggil fungsi yang lambat tidak perlu.)sumber
abs
dalam kode asli (well, saya akan berubah<=
menjadi<
). Dalam contoh Anda, saya melihat inisialisasi, yang berlebihan dalam beberapa kasus dan bisa ekspansif. Bisakah Anda menjelaskan: jelaskan ide Anda lebih banyak?printPositiveAndReturn
hanya dipanggil untuk angka positif. Sebaliknya, selalu mengeksekusi satu cabang, lalu "memperbaiki" nilai dengan mengeksekusi cabang yang berbeda tidak membatalkan efek samping cabang pertama .Kata Pengantar: Tanpa berdebat bahwa
if else
ini adalah jalan yang harus ditempuh, kita masih dapat bermain dan menemukan kesenangan dalam konstruksi yang didukung bahasa.If
Konstruk berikut tersedia digithub.com/icza/gox
perpustakaan saya dengan banyak metode lain, sebagaibuiltinx.If
tipenya.Go memungkinkan untuk melampirkan metode ke tipe yang ditentukan pengguna , termasuk tipe primitif seperti
bool
. Kita dapat membuat tipe kustom yang memilikibool
sebagai tipe yang mendasarinya , dan kemudian dengan konversi tipe sederhana pada kondisi tersebut, kita memiliki akses ke metode-metodenya. Metode yang menerima dan memilih dari operan.Sesuatu seperti ini:
Bagaimana kita bisa menggunakannya?
Misalnya seorang ternary melakukan
max()
:Seorang ternary melakukan
abs()
:Ini terlihat keren, sederhana, elegan, dan efisien (ini juga memenuhi syarat untuk inlining ).
Satu kelemahan dibandingkan dengan operator ternary "nyata": selalu mengevaluasi semua operan.
Untuk mencapai evaluasi yang ditangguhkan dan hanya-jika-diperlukan, satu-satunya pilihan adalah menggunakan fungsi (baik fungsi atau metode yang dideklarasikan , atau fungsi literal ), yang hanya dipanggil ketika / jika diperlukan:
Menggunakannya: Mari kita asumsikan kita memiliki fungsi-fungsi ini untuk menghitung
a
danb
:Kemudian:
Misalnya, kondisi menjadi tahun berjalan> 2020:
Jika kita ingin menggunakan fungsi literal:
Catatan akhir: jika Anda akan memiliki fungsi dengan tanda tangan yang berbeda, Anda tidak dapat menggunakannya di sini. Dalam hal ini Anda dapat menggunakan fungsi literal dengan tanda tangan yang cocok untuk membuatnya masih berlaku.
Misalnya jika
calca()
dancalcb()
akan memiliki parameter juga (selain nilai balik):Ini adalah bagaimana Anda dapat menggunakannya:
Cobalah contoh-contoh ini di Go Playground .
sumber
jawaban eold menarik dan kreatif, bahkan mungkin pintar.
Namun, disarankan untuk melakukan:
Ya, keduanya mengkompilasi pada dasarnya rakitan yang sama, namun kode ini jauh lebih mudah dibaca daripada memanggil fungsi anonim hanya untuk mengembalikan nilai yang bisa ditulis ke variabel di tempat pertama.
Pada dasarnya, kode sederhana dan jelas lebih baik daripada kode kreatif.
Selain itu, kode apa pun yang menggunakan peta literal bukan ide yang baik, karena peta tidak ringan sama sekali di Go. Sejak Go 1.3, urutan iterasi acak untuk peta kecil dijamin, dan untuk menegakkan ini, memori yang didapat agak kurang efisien untuk peta kecil.
Akibatnya, membuat dan menghapus banyak peta kecil memakan banyak ruang dan waktu. Saya memiliki sepotong kode yang menggunakan peta kecil (dua atau tiga kunci, mungkin, tetapi kasus penggunaan umum hanya satu entri) Tetapi kode itu lambat anjing. Kita berbicara setidaknya 3 urutan besarnya lebih lambat dari kode yang sama ditulis ulang untuk menggunakan kunci slice ganda [indeks] => data peta [indeks]. Dan kemungkinan lebih banyak. Karena beberapa operasi yang sebelumnya membutuhkan waktu beberapa menit untuk dijalankan, mulai menyelesaikan dalam milidetik. \
sumber
simple and clear code is better than creative code
- ini saya sangat suka, tapi saya agak bingung di bagian terakhir setelah itudog slow
, mungkin ini bisa membingungkan orang lain juga?m := map[string]interface{} { a: 42, b: "stuff" }
, dan kemudian di fungsi lain iterasi melalui itu:for key, val := range m { code here }
Setelah beralih ke sistem dua slice:,keys = []string{ "a", "b" }, data = []interface{}{ 42, "stuff" }
dan kemudian beralih melaluifor i, key := range keys { val := data[i] ; code here }
hal - hal seperti dipercepat 1000 kali lipat.One-liner, meskipun dijauhi oleh pencipta, memiliki tempat mereka.
Yang ini menyelesaikan masalah evaluasi malas dengan membiarkan Anda, secara opsional, melewati fungsi yang akan dievaluasi jika perlu:
Keluaran
interface{}
untuk memenuhi operasi pemeran internal.c
.Solusi mandiri di sini juga bagus, tetapi bisa jadi kurang jelas untuk beberapa kegunaan.
sumber