Có cách nào nhỏ gọn hơn hoặc pythonic để viết biểu thức boolean
a + b == c or a + c == b or b + c == a
Tôi đến với
a + b + c in (2*a, 2*b, 2*c)
nhưng đó là một chút lạ.
Có cách nào nhỏ gọn hơn hoặc pythonic để viết biểu thức boolean
a + b == c or a + c == b or b + c == a
Tôi đến với
a + b + c in (2*a, 2*b, 2*c)
nhưng đó là một chút lạ.
Câu trả lời:
Nếu chúng ta nhìn vào Zen của Python, hãy nhấn mạnh vào tôi:
Zen của Python, bởi Tim Peters
Đẹp thì tốt hơn xấu.
Rõ ràng là tốt hơn so với ngầm.
Đơn giản là tốt hơn phức tạp.
Phức tạp tốt hơn phức tạp.
Bằng phẳng là tốt hơn so với lồng nhau.
Thưa thì tốt hơn dày đặc.
Tính dễ đọc.
Trường hợp đặc biệt không đủ đặc biệt để phá vỡ các quy tắc.
Mặc dù thực tế đánh bại sự tinh khiết.
Lỗi không bao giờ nên âm thầm vượt qua.
Trừ khi im lặng rõ ràng.
Trước sự mơ hồ, hãy từ chối sự cám dỗ để đoán.
Nên có một-- và tốt nhất là chỉ có một cách rõ ràng để làm điều đó.
Mặc dù cách đó ban đầu có thể không rõ ràng trừ khi bạn là người Hà Lan.
Bây giờ tốt hơn bao giờ hết.
Mặc dù không bao giờ thường tốt hơnngay bây giờ
Nếu việc thực hiện khó giải thích, đó là một ý tưởng tồi.
Nếu việc thực hiện dễ giải thích, nó có thể là một ý tưởng tốt.
Không gian tên là một ý tưởng tuyệt vời - hãy làm nhiều hơn nữa!
Giải pháp Pythonic nhất là giải pháp rõ ràng nhất, đơn giản nhất và dễ giải thích nhất:
a + b == c or a + c == b or b + c == a
Thậm chí tốt hơn, bạn thậm chí không cần biết Python để hiểu mã này! Đó là điều đó dễ dàng. Đây là, không có đặt phòng, giải pháp tốt nhất. Bất cứ điều gì khác là thủ dâm trí tuệ.
Hơn nữa, đây có thể là giải pháp hiệu quả tốt nhất, vì nó là giải pháp duy nhất trong số tất cả các đề xuất ngắn mạch. Nếu a + b == c
, chỉ một bổ sung và so sánh được thực hiện.
Giải ba đẳng thức cho a:
a in (b+c, b-c, c-b)
Python có một any
hàm thực hiện or
trên tất cả các phần tử của chuỗi. Ở đây tôi đã chuyển đổi tuyên bố của bạn thành một bộ 3 yếu tố.
any((a + b == c, a + c == b, b + c == a))
Lưu ý rằng đó or
là ngắn mạch, vì vậy nếu tính toán các điều kiện riêng lẻ là tốn kém, có thể tốt hơn để giữ cấu trúc ban đầu của bạn.
any()
và all()
ngắn mạch quá.
any
cả khi chạy.
any
và all
"đoản mạch" quá trình kiểm tra vòng lặp được đưa ra; nhưng nếu iterable đó là một chuỗi chứ không phải là một trình tạo, thì nó đã được đánh giá đầy đủ trước khi xảy ra lệnh gọi hàm .
any
, thụt lề một lần ):
trong if
câu lệnh), giúp ích rất nhiều cho khả năng đọc khi toán học được tham gia
Nếu bạn biết bạn chỉ đang xử lý các số dương, điều này sẽ hoạt động và khá sạch sẽ:
a, b, c = sorted((a, b, c))
if a + b == c:
do_stuff()
Như tôi đã nói, điều này chỉ hoạt động cho các số dương; nhưng nếu bạn biết họ sẽ tích cực, thì đây là một giải pháp IMO rất dễ đọc, thậm chí trực tiếp trong mã trái ngược với chức năng.
Bạn có thể làm điều này, có thể làm một chút tính toán lặp đi lặp lại; nhưng bạn đã không chỉ định hiệu suất làm mục tiêu của mình:
from itertools import permutations
if any(x + y == z for x, y, z in permutations((a, b, c), 3)):
do_stuff()
Hoặc không có permutations()
và khả năng tính toán lặp đi lặp lại:
if any(x + y == z for x, y, z in [(a, b, c), (a, c, b), (b, c, a)]:
do_stuff()
Tôi có thể sẽ đặt điều này, hoặc bất kỳ giải pháp nào khác, vào một chức năng. Sau đó, bạn có thể chỉ cần gọi sạch hàm trong mã của bạn.
Cá nhân, trừ khi tôi cần sự linh hoạt hơn từ mã, tôi sẽ chỉ sử dụng phương thức đầu tiên trong câu hỏi của bạn. Thật đơn giản và hiệu quả. Tôi vẫn có thể đặt nó vào một chức năng:
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()
Đó là Pythonic đẹp, và đó hoàn toàn có thể là cách hiệu quả nhất để thực hiện (chức năng bổ sung gọi sang một bên); mặc dù bạn không nên lo lắng quá nhiều về hiệu suất, trừ khi nó thực sự gây ra vấn đề.
Nếu bạn sẽ chỉ sử dụng ba biến thì phương thức ban đầu của bạn:
a + b == c or a + c == b or b + c == a
Đã rất pythonic.
Nếu bạn có kế hoạch sử dụng nhiều biến hơn thì phương pháp suy luận của bạn với:
a + b + c in (2*a, 2*b, 2*c)
Rất thông minh nhưng hãy nghĩ về lý do tại sao. Tại sao điều này làm việc?
Thông qua một số số học đơn giản, chúng ta thấy rằng:
a + b = c
c = c
a + b + c == c + c == 2*c
a + b + c == 2*c
Và điều này sẽ phải giữ đúng đối với một trong hai a, b, hoặc c, có nghĩa là có nó sẽ tương đương 2*a
, 2*b
hoặc 2*c
. Điều này sẽ đúng với bất kỳ số lượng biến.
Vì vậy, một cách tốt để viết điều này một cách nhanh chóng là chỉ cần có một danh sách các biến của bạn và kiểm tra tổng của chúng dựa vào danh sách các giá trị nhân đôi.
values = [a,b,c,d,e,...]
any(sum(values) in [2*x for x in values])
Bằng cách này, để thêm nhiều biến vào phương trình, tất cả những gì bạn phải làm là chỉnh sửa danh sách giá trị của mình bằng các biến mới 'n', không viết phương trình 'n'
a=-1
, b=-1
thì c=-2
, sau đó a+b=c
, nhưng a+b+c = -4
và 2*max(a,b,c)
là-2
abs()
cuộc gọi, nó là Pythonic so với đoạn trích của OP (tôi thực sự gọi nó là ít đọc hơn đáng kể).
any(sum(values) == 2*x for x in values)
, theo cách đó bạn sẽ không phải thực hiện tất cả các thao tác nhân đôi lên phía trước, khi cần thiết.
Đoạn mã sau có thể được sử dụng để so sánh lặp lại từng phần tử với tổng của các phần tử khác, được tính từ tổng của toàn bộ danh sách, ngoại trừ phần tử đó.
l = [a,b,c]
any(sum(l)-e == e for e in l)
[]
dấu ngoặc khỏi dòng thứ hai, điều này thậm chí sẽ ngắn mạch như ban đầu với or
...
any(a + b + c == 2*x for x in [a, b, c])
, khá gần với đề xuất của OP
Đừng thử và đơn giản hóa nó. Thay vào đó, hãy đặt tên cho những gì bạn đang làm với một chức năng:
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):
...
Thay thế điều kiện bằng một cái gì đó "thông minh" có thể làm cho nó ngắn hơn, nhưng nó sẽ không làm cho nó dễ đọc hơn. Tuy nhiên, việc để lại nó không dễ đọc lắm, bởi vì thật khó để biết tại sao bạn lại kiểm tra ba điều kiện đó trong nháy mắt. Điều này làm cho nó hoàn toàn rõ ràng những gì bạn đang kiểm tra.
Về hiệu suất, phương pháp này không thêm chi phí cho một cuộc gọi chức năng, nhưng không bao giờ hy sinh khả năng đọc cho hiệu suất trừ khi bạn thấy có một nút cổ chai mà bạn nhất định phải sửa. Và luôn luôn đo lường, vì một số triển khai thông minh có khả năng tối ưu hóa và thực hiện một số lời gọi hàm trong một số trường hợp.
Con trăn 3:
(a+b+c)/2 in (a,b,c)
(a+b+c+d)/2 in (a,b,c,d)
...
Nó chia tỷ lệ cho bất kỳ số lượng biến:
arr = [a,b,c,d,...]
sum(arr)/2 in arr
Tuy nhiên, nói chung tôi đồng ý rằng trừ khi bạn có nhiều hơn ba biến, phiên bản gốc dễ đọc hơn.
[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
Nếu tổng của hai số hạng bất kỳ bằng số hạng thứ ba, thì một trong các yếu tố sẽ bằng 0, làm cho toàn bộ sản phẩm bằng không.
(a+b<>c) && (a+c<>b) && (b+c<>a) == false
Còn về
a == b + c or abs(a) == abs(b - c)
Lưu ý rằng điều này sẽ không hoạt động nếu các biến không được ký.
Từ quan điểm tối ưu hóa mã (ít nhất là trên nền tảng x86), đây dường như là giải pháp hiệu quả nhất.
Trình biên dịch hiện đại sẽ thực hiện cả hai lệnh gọi hàm abs () và tránh kiểm tra dấu hiệu và nhánh có điều kiện tiếp theo bằng cách sử dụng một chuỗi thông minh các lệnh CDQ, XOR và SUB . Do đó, mã mức cao ở trên sẽ được biểu diễn chỉ bằng các hướng dẫn ALU có độ trễ thấp, thông lượng cao và chỉ có hai điều kiện.
fabs()
có thể được sử dụng cho float
các loại;).
Giải pháp được cung cấp bởi Alex Varga "a in (b + c, bc, cb)" nhỏ gọn và đẹp về mặt toán học, nhưng tôi thực sự sẽ không viết mã theo cách đó bởi vì nhà phát triển tiếp theo sẽ không hiểu ngay mục đích của mã .
Giải pháp của Mark Ransom
any((a + b == c, a + c == b, b + c == a))
rõ ràng hơn nhưng không ngắn gọn hơn
a + b == c or a + c == b or b + c == a
Khi viết mã mà người khác sẽ phải xem, hoặc tôi sẽ phải nhìn vào một thời gian dài sau đó khi tôi quên mất những gì tôi đã nghĩ khi tôi viết nó, quá ngắn hoặc thông minh có xu hướng gây hại nhiều hơn là tốt. Mã nên được đọc. Vì vậy, cô đọng là tốt, nhưng không ngắn gọn đến nỗi các lập trình viên tiếp theo không thể hiểu nó.
Yêu cầu là nhỏ gọn hơn HOẶC nhiều pythonic hơn - Tôi đã thử với tay nhỏ gọn hơn.
được
import functools, itertools
f = functools.partial(itertools.permutations, r = 3)
def g(x,y,z):
return x + y == z
Đây là 2 ký tự ít hơn bản gốc
any(g(*args) for args in f((a,b,c)))
kiểm tra với:
assert any(g(*args) for args in f((a,b,c))) == (a + b == c or a + c == b or b + c == a)
ngoài ra, được đưa ra:
h = functools.partial(itertools.starmap, g)
Điều này là tương đương
any(h(f((a,b,c))))
g()
mà bạn phải xác định để làm việc này. Với tất cả những điều đó, tôi muốn nói rằng nó lớn hơn đáng kể.
Tôi muốn trình bày những gì tôi thấy là câu trả lời pythonic nhất :
def one_number_is_the_sum_of_the_others(a, b, c):
return any((a == b + c, b == a + c, c == a + b))
Trường hợp chung, không được tối ưu hóa:
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
Về mặt Zen của Python, tôi nghĩ rằng các câu được nhấn mạnh được theo dõi nhiều hơn so với câu trả lời khác:
Zen của Python, bởi Tim Peters
Đẹp thì tốt hơn xấu.
Rõ ràng là tốt hơn so với ngầm.
Đơn giản là tốt hơn phức tạp.
Phức tạp tốt hơn phức tạp.
Bằng phẳng là tốt hơn so với lồng nhau.
Thưa thì tốt hơn dày đặc.
Tính dễ đọc.
Trường hợp đặc biệt không đủ đặc biệt để phá vỡ các quy tắc.
Mặc dù thực tế đánh bại sự tinh khiết.
Lỗi không bao giờ nên âm thầm vượt qua.
Trừ khi im lặng rõ ràng.
Trước sự mơ hồ, hãy từ chối sự cám dỗ để đoán.
Nên có một-- và tốt nhất là chỉ có một cách rõ ràng để làm điều đó.
Mặc dù cách đó ban đầu có thể không rõ ràng trừ khi bạn là người Hà Lan.
Bây giờ tốt hơn bao giờ hết.
Mặc dù không bao giờ thường tốt hơnngay bây giờ
Nếu việc thực hiện khó giải thích, đó là một ý tưởng tồi.
Nếu việc thực hiện dễ giải thích, nó có thể là một ý tưởng tốt.
Không gian tên là một ý tưởng tuyệt vời - hãy làm nhiều hơn nữa!
Như một thói quen cũ của lập trình của tôi, tôi nghĩ rằng việc đặt biểu thức phức tạp ở bên phải trong một mệnh đề có thể làm cho nó dễ đọc hơn như thế này:
a == b+c or b == a+c or c == a+b
Cộng ()
:
((a == b+c) or (b == a+c) or (c == a+b))
Và tôi cũng nghĩ rằng việc sử dụng nhiều dòng cũng có thể tạo ra nhiều giác quan như thế này:
((a == b+c) or
(b == a+c) or
(c == a+b))
Nói một cách chung chung,
m = a+b-c;
if (m == 0 || m == 2*a || m == 2*b) do_stuff ();
nếu, thao tác với một biến đầu vào là OK cho bạn,
c = a+b-c;
if (c==0 || c == 2*a || c == 2*b) do_stuff ();
nếu bạn muốn khai thác bằng cách sử dụng hack, bạn có thể sử dụng "!", ">> 1" và "<< 1"
Tôi đã tránh phân chia mặc dù nó cho phép sử dụng để tránh hai phép nhân để tránh các lỗi làm tròn. Tuy nhiên, kiểm tra tràn
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