Apakah ada cara yang lebih ringkas atau pythonic untuk menulis ekspresi boolean
a + b == c or a + c == b or b + c == a
Saya datang dengan
a + b + c in (2*a, 2*b, 2*c)
tapi itu agak aneh.
Apakah ada cara yang lebih ringkas atau pythonic untuk menulis ekspresi boolean
a + b == c or a + c == b or b + c == a
Saya datang dengan
a + b + c in (2*a, 2*b, 2*c)
tapi itu agak aneh.
Jawaban:
Jika kita melihat Zen of Python, tekankan milik saya:
The Zen of Python, oleh Tim Peters
Cantik itu lebih baik dari pada jelek.
Eksplisit lebih baik daripada implisit.
Sederhana lebih baik daripada kompleks.
Kompleks lebih baik daripada rumit.
Datar lebih baik dari pada bersarang.
Sparse lebih baik dari pada padat.
Keterbacaan itu penting.
Kasus khusus tidak cukup istimewa untuk melanggar aturan.
Meskipun kepraktisan mengalahkan kemurnian.
Kesalahan tidak boleh lewat diam-diam.
Kecuali dibungkam secara eksplisit.
Dalam menghadapi ambiguitas, tolak godaan untuk menebak.
Seharusnya ada satu - dan sebaiknya hanya satu --cara yang jelas untuk melakukannya.
Meskipun cara itu mungkin tidak terlihat pada awalnya kecuali Anda orang Belanda.
Sekarang lebih baik daripada tidak sama sekali.
Meskipun tidak pernah lebih baik daritepat sekarang.
Jika implementasinya sulit dijelaskan, itu ide yang buruk.
Jika penerapannya mudah dijelaskan, mungkin itu ide yang bagus.
Namespaces adalah salah satu ide bagus - mari kita lakukan lebih banyak lagi!
Solusi paling Pythonic adalah yang paling jelas, paling sederhana, dan paling mudah dijelaskan:
a + b == c or a + c == b or b + c == a
Bahkan lebih baik lagi, Anda bahkan tidak perlu tahu Python untuk memahami kode ini! Semudah itu . Ini, tanpa syarat, adalah solusi terbaik. Yang lainnya adalah masturbasi intelektual.
Selain itu, ini kemungkinan merupakan solusi dengan kinerja terbaik juga, karena ini adalah satu-satunya dari semua proposal yang mengalami korsleting. Jika a + b == c
hanya dilakukan satu penjumlahan dan perbandingan.
Memecahkan tiga persamaan untuk:
a in (b+c, b-c, c-b)
Python memiliki any
fungsi yang mengerjakan or
semua elemen urutan. Di sini saya telah mengubah pernyataan Anda menjadi tupel 3 elemen.
any((a + b == c, a + c == b, b + c == a))
Perhatikan bahwa itu or
adalah korsleting, jadi jika menghitung kondisi individu mahal, mungkin lebih baik menyimpan konstruksi asli Anda.
any()
dan all()
korsleting juga.
any
bahkan sebelum dijalankan.
any
dan all
"memperpendek" proses memeriksa iterable yang diberikan; tetapi jika iterable itu adalah urutan dan bukan generator, maka iterable telah dievaluasi sepenuhnya sebelum pemanggilan fungsi terjadi .
any
, membuat indentasi tunggal ):
pada if
pernyataan), yang membantu banyak untuk keterbacaan saat matematika terlibat
Jika Anda tahu Anda hanya berurusan dengan bilangan positif, ini akan berhasil, dan cukup bersih:
a, b, c = sorted((a, b, c))
if a + b == c:
do_stuff()
Seperti yang saya katakan, ini hanya berfungsi untuk bilangan positif; tetapi jika Anda tahu mereka akan menjadi positif, ini adalah IMO solusi yang sangat mudah dibaca, bahkan secara langsung dalam kode sebagai lawan dalam suatu fungsi.
Anda dapat melakukan ini, yang mungkin melakukan sedikit penghitungan berulang; tetapi Anda tidak menetapkan kinerja sebagai sasaran Anda:
from itertools import permutations
if any(x + y == z for x, y, z in permutations((a, b, c), 3)):
do_stuff()
Atau tanpa permutations()
dan kemungkinan perhitungan berulang:
if any(x + y == z for x, y, z in [(a, b, c), (a, c, b), (b, c, a)]:
do_stuff()
Saya mungkin akan menempatkan ini, atau solusi lain, ke dalam suatu fungsi. Kemudian Anda dapat memanggil fungsi dalam kode Anda dengan rapi.
Secara pribadi, kecuali saya membutuhkan lebih banyak fleksibilitas dari kode, saya hanya akan menggunakan metode pertama dalam pertanyaan Anda. Sederhana dan efisien. Saya mungkin masih memasukkannya ke dalam fungsi:
def two_add_to_third(a, b, c):
return a + b == c or a + c == b or b + c == a
if two_add_to_third(a, b, c):
do_stuff()
Itu cukup Pythonic, dan itu sangat mungkin cara paling efisien untuk melakukannya (fungsi tambahan memanggil samping); meskipun Anda tidak perlu terlalu khawatir tentang kinerja, kecuali itu benar-benar menyebabkan masalah.
Jika Anda hanya akan menggunakan tiga variabel maka metode awal Anda:
a + b == c or a + c == b or b + c == a
Apakah sudah sangat pythonic.
Jika Anda berencana menggunakan lebih banyak variabel maka metode penalaran Anda dengan:
a + b + c in (2*a, 2*b, 2*c)
Sangat pintar tetapi mari kita pikirkan mengapa. Mengapa ini berhasil?
Melalui beberapa aritmatika sederhana kita melihat bahwa:
a + b = c
c = c
a + b + c == c + c == 2*c
a + b + c == 2*c
Dan ini harus berlaku baik untuk a, b, atau c, yang berarti bahwa ya itu akan sama 2*a
, 2*b
atau 2*c
. Ini akan benar untuk sejumlah variabel.
Jadi cara yang baik untuk menulis ini dengan cepat adalah dengan membuat daftar variabel Anda dan memeriksa jumlahnya dengan daftar nilai yang digandakan.
values = [a,b,c,d,e,...]
any(sum(values) in [2*x for x in values])
Dengan cara ini, untuk menambahkan lebih banyak variabel ke dalam persamaan yang harus Anda lakukan adalah mengedit daftar nilai Anda dengan variabel baru 'n', bukan menulis persamaan 'n'
a=-1
, b=-1
, c=-2
, kemudian a+b=c
, namun a+b+c = -4
, dan 2*max(a,b,c)
adalah-2
abs()
panggilan, itu Pythonic daripada cuplikan OP (saya sebenarnya akan menyebutnya kurang dapat dibaca secara signifikan).
any(sum(values) == 2*x for x in values)
, dengan cara itu Anda tidak perlu melakukan semua penggandaan di depan, seperti yang diperlukan.
Kode berikut dapat digunakan untuk membandingkan setiap elemen secara berulang dengan jumlah lainnya, yang dihitung dari jumlah seluruh daftar, tidak termasuk elemen itu.
l = [a,b,c]
any(sum(l)-e == e for e in l)
[]
tanda kurung dari baris kedua, ini bahkan akan korsleting seperti aslinya dengan or
...
any(a + b + c == 2*x for x in [a, b, c])
, cukup dekat dengan saran OP
Jangan mencoba dan menyederhanakannya. Sebagai gantinya, sebutkan apa yang Anda lakukan dengan suatu fungsi:
def any_two_sum_to_third(a, b, c):
return a + b == c or a + c == b or b + c == a
if any_two_sum_to_third(foo, bar, baz):
...
Mengganti ketentuan dengan sesuatu yang "pintar" mungkin membuatnya lebih pendek, tetapi tidak akan membuatnya lebih mudah dibaca. Membiarkannya seperti itu juga tidak terlalu mudah dibaca, karena sulit untuk mengetahui mengapa Anda memeriksa ketiga kondisi tersebut secara sekilas. Ini membuatnya sangat jelas tentang apa yang Anda periksa.
Terkait performa, pendekatan ini memang menambahkan overhead panggilan fungsi, tetapi tidak pernah mengorbankan keterbacaan untuk performa kecuali Anda telah menemukan hambatan yang benar-benar harus Anda perbaiki. Dan selalu ukur, karena beberapa implementasi pintar mampu mengoptimalkan dan menyebariskan beberapa pemanggilan fungsi dalam beberapa keadaan.
Python 3:
(a+b+c)/2 in (a,b,c)
(a+b+c+d)/2 in (a,b,c,d)
...
Ini menskalakan ke sejumlah variabel:
arr = [a,b,c,d,...]
sum(arr)/2 in arr
Namun, secara umum saya setuju bahwa kecuali Anda memiliki lebih dari tiga variabel, versi aslinya lebih mudah dibaca.
[x for x in range(pow(2,30)) if x != ((x * 2)/ pow(2,1))]
(a+b-c)*(a+c-b)*(b+c-a) == 0
Jika hasil penjumlahan dua suku apa pun sama dengan suku ketiga, salah satu faktornya adalah nol, sehingga seluruh hasil kali menjadi nol.
(a+b<>c) && (a+c<>b) && (b+c<>a) == false
Bagaimana dengan:
a == b + c or abs(a) == abs(b - c)
Perhatikan bahwa ini tidak akan berfungsi jika variabel tidak ditandatangani.
Dari sudut pandang pengoptimalan kode (setidaknya pada platform x86) ini tampaknya menjadi solusi yang paling efisien.
Kompiler modern akan menyebariskan kedua pemanggilan fungsi abs () dan menghindari pengujian tanda dan cabang bersyarat berikutnya dengan menggunakan urutan instruksi CDQ, XOR, dan SUB yang cerdas . Kode tingkat tinggi di atas dengan demikian akan diwakili dengan hanya latensi rendah, instruksi ALU throughput tinggi dan hanya dua kondisional.
fabs()
bisa digunakan untuk float
tipe;).
Solusi yang diberikan oleh Alex Varga "a in (b + c, bc, cb)" kompak dan indah secara matematis, tetapi saya tidak akan benar-benar menulis kode seperti itu karena pengembang berikutnya yang datang tidak akan segera memahami tujuan kode tersebut .
Solusi Mark Ransom dari
any((a + b == c, a + c == b, b + c == a))
lebih jelas tetapi tidak lebih ringkas dari
a + b == c or a + c == b or b + c == a
Saat menulis kode yang harus dilihat oleh orang lain, atau yang akan saya lihat nanti ketika saya lupa apa yang saya pikirkan ketika saya menulisnya, terlalu pendek atau pintar cenderung lebih merugikan daripada menguntungkan. Kode harus dapat dibaca. Singkat itu bagus, tetapi tidak terlalu ringkas sehingga programmer berikutnya tidak dapat memahaminya.
Permintaan untuk lebih kompak ATAU lebih pythonic - Saya mencoba tangan saya lebih kompak.
diberikan
import functools, itertools
f = functools.partial(itertools.permutations, r = 3)
def g(x,y,z):
return x + y == z
Ini adalah 2 karakter kurang dari aslinya
any(g(*args) for args in f((a,b,c)))
uji dengan:
assert any(g(*args) for args in f((a,b,c))) == (a + b == c or a + c == b or b + c == a)
Selain itu, diberikan:
h = functools.partial(itertools.starmap, g)
Ini setara
any(h(f((a,b,c))))
g()
yang harus Anda tentukan agar ini berfungsi. Mengingat semua itu, menurut saya itu jauh lebih besar.
Saya ingin menyajikan apa yang saya lihat sebagai jawaban paling pythonic :
def one_number_is_the_sum_of_the_others(a, b, c):
return any((a == b + c, b == a + c, c == a + b))
Kasus umum, tidak dioptimalkan:
def one_number_is_the_sum_of_the_others(numbers):
for idx in range(len(numbers)):
remaining_numbers = numbers[:]
sum_candidate = remaining_numbers.pop(idx)
if sum_candidate == sum(remaining_numbers):
return True
return False
Dalam hal Zen of Python, saya pikir pernyataan yang ditekankan lebih banyak diikuti daripada dari jawaban lain:
The Zen of Python, oleh Tim Peters
Cantik itu lebih baik dari pada jelek.
Eksplisit lebih baik daripada implisit.
Sederhana lebih baik daripada kompleks.
Kompleks lebih baik daripada rumit.
Datar lebih baik dari pada bersarang.
Sparse lebih baik dari pada padat.
Keterbacaan itu penting.
Kasus khusus tidak cukup istimewa untuk melanggar aturan.
Meskipun kepraktisan mengalahkan kemurnian.
Kesalahan tidak boleh lewat diam-diam.
Kecuali dibungkam secara eksplisit.
Dalam menghadapi ambiguitas, tolak godaan untuk menebak.
Seharusnya ada satu - dan sebaiknya hanya satu --cara yang jelas untuk melakukannya.
Meskipun cara itu mungkin tidak terlihat pada awalnya kecuali Anda orang Belanda.
Sekarang lebih baik daripada tidak sama sekali.
Meskipun tidak pernah lebih baik daritepat sekarang.
Jika implementasinya sulit dijelaskan, itu ide yang buruk.
Jika penerapannya mudah dijelaskan, mungkin itu ide yang bagus.
Namespaces adalah salah satu ide bagus - mari kita lakukan lebih banyak lagi!
Sebagai kebiasaan lama pemrograman saya, saya pikir menempatkan ekspresi kompleks di kanan dalam klausa dapat membuatnya lebih mudah dibaca seperti ini:
a == b+c or b == a+c or c == a+b
Plus ()
:
((a == b+c) or (b == a+c) or (c == a+b))
Dan juga saya pikir menggunakan multi-garis juga bisa membuat lebih banyak indra seperti ini:
((a == b+c) or
(b == a+c) or
(c == a+b))
Secara umum,
m = a+b-c;
if (m == 0 || m == 2*a || m == 2*b) do_stuff ();
jika, memanipulasi variabel input tidak masalah untuk Anda,
c = a+b-c;
if (c==0 || c == 2*a || c == 2*b) do_stuff ();
jika Anda ingin mengeksploitasi menggunakan bit hacks, Anda dapat menggunakan "!", ">> 1" dan "<< 1"
Saya menghindari pembagian meskipun itu memungkinkan penggunaan untuk menghindari dua perkalian untuk menghindari kesalahan pembulatan. Namun, periksa overflow
def any_sum_of_others (*nums):
num_elements = len(nums)
for i in range(num_elements):
discriminating_map = map(lambda j: -1 if j == i else 1, range(num_elements))
if sum(n * u for n, u in zip(nums, discriminating_map)) == 0:
return True
return False
print(any_sum_of_others(0, 0, 0)) # True
print(any_sum_of_others(1, 2, 3)) # True
print(any_sum_of_others(7, 12, 5)) # True
print(any_sum_of_others(4, 2, 2)) # True
print(any_sum_of_others(1, -1, 0)) # True
print(any_sum_of_others(9, 8, -4)) # False
print(any_sum_of_others(4, 3, 2)) # False
print(any_sum_of_others(1, 1, 1, 1, 4)) # True
print(any_sum_of_others(0)) # True
print(any_sum_of_others(1)) # False