Tại sao không có nhiều ngôn ngữ có khả năng so sánh một giá trị với nhiều hơn một giá trị khác? [đóng cửa]


10

Hãy xem xét những điều sau đây:

if(a == b or c)

Trong hầu hết các ngôn ngữ, điều này sẽ cần phải được viết là:

if(a == b or a == c)

đó là hơi cồng kềnh và lặp lại thông tin.

Tôi biết cú pháp mẫu ở trên của tôi hơi khó hiểu, nhưng tôi chắc chắn có nhiều cách tốt hơn để truyền đạt ý tưởng.

Tại sao không có nhiều ngôn ngữ cung cấp nó? Có vấn đề về hiệu suất hoặc cú pháp?


6
SQL cung cấp rằng: trong đó A IN (B, C)
thứ năm

4
Tôi đã không yêu cầu các ngôn ngữ cung cấp nó, hoặc có thể có nó, nhưng tại sao không có nhiều ngôn ngữ hơn cung cấp nó? Có vấn đề về hiệu suất hoặc cú pháp?
Zeroth

8
để khái quát hóa câu trả lời của @ thursdaygeek, trong hầu hết các ngôn ngữ, thông thường bạn làm điều đó với ngăn chặn được đặt. (Hoặc một danh sách hoặc tuple nếu điều đó dễ dàng hơn.) Nó hoạt động tương tự và tránh một số vấn đề cú pháp có thể khó khăn. Từ ví dụ của bạn, "b hoặc c" có nghĩa là tập hợp "{b, c}" hoặc là hoặc một toán tử như | | ? Trong python "b hoặc c" có nghĩa là "giá trị của b nếu đúng hoặc nếu không thì giá trị của c"
Rob

4
Thực chất đây là một vấn đề cú pháp. Vấn đề hiện tại là có một cách trực quan để phân biệt sự khác biệt giữa "b hoặc c" và "b hoặc'd với c".
YoungJohn

2
Nó khá là a == b or crắc rối đối với trường hợp đặc biệt và thậm chí nó còn không tốt cho IMHO.

Câu trả lời:


24

Vấn đề cú pháp là - nó yêu cầu cú pháp.

Bất kể cú pháp ngôn ngữ của bạn là gì, mọi người sử dụng ngôn ngữ phải học nó. Nếu không, họ có nguy cơ nhìn thấy mã và không biết nó làm gì. Do đó, nó thường được coi là một điều tốt nếu một ngôn ngữ có cú pháp đơn giản xử lý rõ ràng nhiều trường hợp.

Trong ví dụ cụ thể của bạn, bạn đang cố lấy một toán tử infix (một hàm có hai đối số nhưng được viết Argument1 Operator Argument2) và cố gắng mở rộng nó thành nhiều đối số. Điều đó không hoạt động rất sạch sẽ bởi vì toàn bộ điểm của các toán tử infix, đến mức có một, là đặt toán tử ngay giữa hai đối số. Mở rộng ra (Argument1 Operator Argument2 MagicallyClearSymbol Argument3...)dường như không thêm nhiều sự rõ ràng hơn Equals(Arg1,Arg2,...). Infix cũng thường được sử dụng để mô phỏng các quy ước toán học mà mọi người quen thuộc, điều này không đúng với cú pháp thay thế.

Sẽ không có bất kỳ vấn đề hiệu suất cụ thể nào liên quan đến ý tưởng của bạn, ngoài việc trình phân tích cú pháp sẽ phải xử lý một ngữ pháp với một hoặc hai quy tắc sản xuất khác, có thể ảnh hưởng nhẹ đến tốc độ phân tích cú pháp. Điều này có thể tạo ra một số khác biệt cho một ngôn ngữ được biên dịch hoặc JIT, nhưng có lẽ không phải là một sự khác biệt lớn.

Vấn đề lớn hơn với ý tưởng chỉ là việc tạo ra nhiều trường hợp đặc biệt trong một ngôn ngữ có xu hướng là một ý tưởng tồi .


1
Ngoài ra: Scala có các toán tử infix với số lượng đối số tùy ý, vì các toán tử infix chỉ là các cuộc gọi phương thức mà không có a .. Vì vậy, họ sẽ được viết là arg1 op (arg2, arg3). Không chính xác đẹp, nhưng cần thiết ở một số nơi trong bối cảnh của ngôn ngữ đó.
amon

những gì về if my_var in (a, b)sau đó? Đây không phải là một câu hỏi về việc sử dụng công cụ phù hợp cho công việc?

Điểm tuyệt vời. Cú pháp ngôn ngữ nên là yếu tố cần thiết của ngôn ngữ và sau đó bạn xây dựng thư viện trên đầu trang. Nếu ngôn ngữ quá lộn xộn với đường cú pháp "hữu ích", nó sẽ trở nên khó sử dụng hơn. Không phải ai cũng cần a == b or ctrong khi những người khác muốn a == b or c but not d. IMO đó là nơi các chức năng / thư viện tiện ích đến giải cứu.
Allan

Có lẽ điều cần thiết là một phương tiện thông qua đó một phương thức có thể chỉ định rằng một cuộc gọi có số lượng đối số tùy ý nên được xử lý thành nhiều cuộc gọi, với kết quả được kết hợp theo một cách nào đó. Nếu f().Equals(a,b,c); có thể được đánh giá là (var temp=f(); temp.Equals(a)||temp.Equals(b)||temp.Equals(c))cú pháp đó sẽ hoàn hảo, nhưng nếu nó được đánh giá là int[] arr = {a,b,c}; f().Equals(arr);sẽ không tốt lắm, đặc biệt là nếu một mảng mới phải được tạo cho mỗi cuộc gọi.
supercat

6

Bởi vì đó không phải là vấn đề và việc giải quyết nó mang lại lợi ích cơ bản bằng không, nhưng thực hiện nó mang lại chi phí khác không.

Các chức năng dựa trên phạm vi hiện có và thực tế là mọi ngôn ngữ cung cấp có thể hoạt động hoàn toàn tốt trong tình huống này nếu nó mở rộng đến một kích thước mà a == b || a == csẽ không cắt nó.


2
+1, nhưng tôi nghĩ rằng câu trả lời sẽ được cải thiện bằng cách hiển thị một hoặc hai trong số các "chức năng dựa trên phạm vi hiện có mà thực tế mọi ngôn ngữ [cung cấp]", vì vậy sự thay thế này sẽ rõ ràng hơn.
Avner Shahar-Kashtan

Bạn có thể chứng minh rằng nó "mang lại lợi ích cơ bản bằng không, nhưng thực hiện nó mang lại chi phí khác không"?
Darek Nędza

3
@ DarekNędza Nửa thứ hai không nên gây tranh cãi: Mọi tính năng phải được suy nghĩ, thực hiện, thử nghiệm, ghi lại và hỗ trợ. Không có bước nào trong số đó là miễn phí theo bất kỳ số liệu hợp lý nào (thời gian của mọi người, chi phí cơ hội, độ phức tạp, chi phí tiền tệ nếu có ai trả tiền để làm việc trên đó, v.v.).

@ AvnerShahar-Kashtan Đồng ý - với tôi, không rõ ràng nó sẽ trông như thế nào, nói, java, hoặc sh, hoặc zsh? Ok, anh ta có thể ngụ ý ngôn ngữ 'hiện đại'. Groovy?
Volker Siegel

Trong PHP, nó sẽ trông như thế in_array($a, [$b, $c, $d, $e, $f]). : P
cHao

6

Một số ngôn ngữ có tính năng như vậy. Ví dụ như trong Perl6 chúng ta có thể sử dụng mối nối , đó là “chồng chất” của hai giá trị:

if $a == any($b, $c) {
    say "yes";
}

# syntactic sugar for the above
if $a == $b | $c {
    say "yes";
}

Các mối nối cho phép chúng ta thể hiện các hoạt động trên một tập hợp dữ liệu khá ngắn gọn, tương tự như cách các hoạt động vô hướng phân phối trên các bộ sưu tập trong một số ngôn ngữ. Ví dụ: sử dụng Python với numpy, phép so sánh có thể được phân phối trên tất cả các giá trị:

import numpy as np
2 == np.array([1, 2, 3])
#=> np.array([False, True, False], dtype=np.bool)
(2 == np.array([1, 2, 3])).any()
#=> True

Tuy nhiên, điều này chỉ hoạt động cho các loại nguyên thủy được chọn.

Tại sao các mối nối có vấn đề? Bởi vì các hoạt động trên một đường nối phân phối trên các giá trị được chứa, chính đối tượng đường nối hoạt động giống như một proxy cho các cuộc gọi phương thức - một vài hệ thống loại trừ việc gõ vịt có thể xử lý.

Loại vấn đề hệ thống có thể tránh được nếu các mối nối như vậy chỉ được phép dưới dạng cú pháp đặc biệt xung quanh các toán tử so sánh. Nhưng trong trường hợp này, chúng bị giới hạn đến mức chúng không thêm giá trị đủ để thêm vào bất kỳ ngôn ngữ lành mạnh nào. Hành vi tương tự có thể được thể hiện bằng cách sử dụng các thao tác đã đặt hoặc đánh vần tất cả các so sánh theo cách thủ công và hầu hết các ngôn ngữ không tin vào việc thêm cú pháp dự phòng nếu đã có một giải pháp hoàn toàn tốt.


Ví dụ numpy cụ thể đó có thể được viết lại rõ ràng hơn như 2 in [1, 2, 3]. Mặt khác, nếu numpy có một .all()hoặc một cái gì đó, con trăn đồng bằng tương đương gần như không ngắn gọn.
Izkata

@Izkata Tôi đặc biệt không sử dụng các thao tác thiết lập. Trong khi ví dụ của tôi đã sử dụng ==toán tử, chúng ta cũng có thể sử dụng <thay thế - inbây giờ bạn đang ở đâu? Các mối nối chung chung hơn so với thiết lập các kiểm tra thành viên, bởi vì các hoạt động trên đường giao nhau phân phối trên tất cả các thành viên - (x|y).foox.foo|y.foo, cho đến khi đường nối cuối cùng được thu gọn thành một giá trị duy nhất. Mã NumPy được cung cấp cho thấy một bản dịch chính xác tương đương nhưng dài dòng hơn về các mối nối Perl6, giả sử các kiểu nguyên thủy.
amon

2

Trong các ngôn ngữ có macro, thật dễ dàng để thêm một cái gì đó như thế nếu nó chưa có ở đó. Xem xét vợt

(define-syntax-rule (equal-any? a b ...)
  (or (equal? a b) ...))
(equal-any? "a" "b" "a")
> #t

Trong các ngôn ngữ khác mà không cần lập trình siêu dữ liệu, có lẽ bạn có thể định dạng lại thành kiểm tra thành viên / danh sách có thể:

if a ∈ {b, c}

2
Hai kiểm tra đầu tiên xem tất cả các đối số có bằng nhau không; OP muốn kiểm tra xem đối số đầu tiên có bằng bất kỳ đối số nào sau đây không. Thật kỳ lạ, đoạn trích thứ ba mà bạn thể hiện không tôn trọng điều đó.

@delnan Xin lỗi tôi hiểu nhầm thứ. Tôi đã chỉnh sửa nó.
Phil

2

Trong một số ngôn ngữ (phổ biến) ==toán tử không phải là bắc cầu. Chẳng hạn, trong JavaScript 0bằng cả hai '''0', nhưng sau đó '''0'không bằng nhau. Nhiều hơn những điều kỳ quặc như vậy trong PHP.

Điều đó có nghĩa là a == b == csẽ thêm một sự mơ hồ khác, bởi vì nó có thể mang lại một kết quả khác tùy thuộc vào việc nó được hiểu là (a == b) & (a == c)hay (a == b) & (a == c) & (b == c).


2

Trong hầu hết các ngôn ngữ, điều này có thể đạt được một cách tầm thường bằng cách viết một Inhàm, vậy tại sao làm cho nó trở thành một phần của ngôn ngữ thực tế?

Linq, ví dụ, có Contains().

Được rồi, đối với tất cả các bạn là giáo viên, đây là triển khai của tôi trong C #:

public static bool In<T>(this T obj, params T[] values)
{
    for(int i=0; i < values.Length; i++)
    {
        if (object.Equals(obj, values[i]))
            return true;
    }
    return false;
}

Điều đó hoạt động trên một phạm vi thời gian chạy của các giá trị, không phải là một tuple vì mã của OP có thể được biểu thị bằng.
DeadMG

Có vẻ như, chỉ vì nó dễ, không có nghĩa là không nên làm. Nó ... xem xét xây dựng. Tại sao chúng ta luôn phải tự viết tất cả các phần cơ bản của chức năng và thuật toán này, lặp đi lặp lại nhiều lần?
Zeroth

5
@Zeroth có thể bạn đang viết cùng một thứ nhiều lần, nhưng những người khác có xu hướng sử dụng các cơ chế trừu tượng được cung cấp bởi ngôn ngữ của họ thay thế. Nếu bạn thấy mình viết a == b || a == cnhiều lần, có lẽ đã đến lúcequals_any(a, {b, c})
amon

Việc triển khai "chứa" không dễ dàng mở rộng để bao gồm những thứ như if (a > (b or c))if (a mod (b or c) == 2).
tobyink

1
Có ai đó nói trẻ em? :) Đó là một vòng lặp foreach, vì vậy không có ibiến. Và nhìn chung, nó dường như được viết sau khi bạn có một ngày dài :) Bởi vì đặt cả hai return truereturn falsebên trong vòng lặp ở đây có nghĩa là không có cách nào nó sẽ vượt qua được lần lặp đầu tiên. Bạn chỉ so sánh với người đầu tiên value. Nhân tiện, tại sao không sử dụng Anynhư @Bob đề xuất và đơn giản hóa nó thànhreturn values.Any(value => Object.Equals(obj, value));
Konrad Morawski

1

"if (a == b hoặc c)" hoạt động ở hầu hết các ngôn ngữ: if a == b hoặc nếu c không âm, null hoặc zero.

Khiếu nại rằng nó dài dòng bỏ lỡ vấn đề: bạn không nên chồng hàng tá thứ vào một điều kiện. Nếu bạn cần so sánh một giá trị với một số tùy ý của các giá trị khác, thì hãy xây dựng một chương trình con.


3
Những ngôn ngữ tạo nên "nhất"?
Thất vọngWithFormsDesigner

1
@FrustratedWithFormsDesigner, tốt, nếu cđánh giá theo kiểu boolean, thì hầu như mọi ngôn ngữ đều có thể xử lý a == b || c:)
Brian S

@BrianS: Tôi giả sử OP có nghĩa là cú pháp theo nghĩa đen if(a == b or c). Tôi cần nghỉ ngơi, tôi nghĩ ...: P
Thất vọngWithFormsDesigner

@FrustratedWithFormsDesigner Lisp! ... huh? ... :)
Volker Siegel

3
Điều này thực sự bỏ lỡ điểm của câu hỏi. if (a == b or c)là mã giả để kiểm tra xem acó bằng bhay không abằng c. Nó không có nghĩa là để kiểm tra đó clà khác không.
hvd

1

Thông thường, bạn muốn giữ cú pháp của mình ở mức tối thiểu và thay vào đó cho phép các cấu trúc như vậy được xác định bằng chính ngôn ngữ.

Ví dụ: trong Haskell, bạn có thể chuyển đổi bất kỳ hàm nào có hai hoặc nhiều đối số thành toán tử infix bằng backticks. Điều này cho phép bạn viết:

if a `elem` [b, c] then ... else ...

trong đó elemchỉ là một hàm bình thường có hai đối số - một giá trị và một danh sách các giá trị - và kiểm tra xem cái đầu tiên có phải là một phần tử của cái thứ hai không.

Nếu bạn muốn sử dụng andthay vì or? Trong Haskell, bạn chỉ có thể sử dụng các thao tác sau thay vì chờ nhà cung cấp trình biên dịch triển khai một tính năng mới:

 if all (== a) [b, c] then ... else ...

1
Tại sao người ta muốn giữ cú pháp ở mức tối thiểu? Chính xác thì sự đánh đổi đang diễn ra ở đó là gì? Đừng tuyên bố như thế mà không ủng hộ lập luận. ;)
Zeroth

1

Một số ngôn ngữ cung cấp điều này - đến một mức độ.

Có thể không phải là ví dụ cụ thể của bạn , nhưng lấy ví dụ một dòng Python:

def minmax(min, max):
    def answer(value):
        return max > value > min
    return answer

inbounds = minmax(5, 15)
inbounds(7) ##returns True
inbounds(3) ##returns False
inbounds(18) ##returns False

Vì vậy, một số ngôn ngữ vẫn ổn với nhiều so sánh, miễn là bạn diễn đạt chính xác.

Thật không may, nó không hoạt động giống như bạn mong đợi nó để so sánh.

>>> def foo(a, b):
...     def answer(value):
...         return value == a or b
...     return answer
... 
>>> tester = foo(2, 4)
>>> tester(3)
4
>>> tester(2)
True
>>> tester(4)
4
>>> 

"Ý bạn nó trả về True hay 4?" - thuê sau bạn

Một giải pháp trong trường hợp này, ít nhất là với Python, là sử dụng nó hơi khác:

>>> def bar(a, b):
...     def ans(val):
...             return val == a or val == b
...     return ans
... 
>>> this = bar(4, 10)
>>> this(5)
False
>>> this(4)
True
>>> this(10)
True
>>> this(9)
False
>>> 

EDIT: Sau đây cũng sẽ làm một cái gì đó tương tự, một lần nữa trong Python ...

>>> def bar(a, b):
...     def answer(val):
...             return val in (a, b)
...     return answer
... 
>>> this = bar(3, 5)
>>> this(3)
True
>>> this(4)
False
>>> this(5)
True
>>> 

Vì vậy, bất cứ ngôn ngữ mà bạn đang sử dụng, nó có thể không thể là bạn không thể làm điều đó, chỉ là trước tiên bạn phải có một cái nhìn sâu hơn về cách logic thực sự hoạt động. Thông thường, đó chỉ là vấn đề biết những gì bạn 'thực sự hỏi' ngôn ngữ để nói với bạn.


1

Phương thức indexOf, được sử dụng trên Mảng, mà tất cả các ngôn ngữ đều có, cho phép so sánh một giá trị với một số ngôn ngữ khác, vì vậy tôi đoán rằng một toán tử đặc biệt không có nhiều ý nghĩa.

Trong javascript sẽ viết:

if ( [b, c].indexOf(a) != -1 ) { ....  }

0

Bạn hỏi tại sao chúng ta không thể làm điều này: if(a == b or c)

Python thực hiện điều này rất hiệu quả, trên thực tế, hiệu quả nhất với set:

if a in set([b, c]):
    then_do_this()

Để kiểm tra tư cách thành viên, 'set' kiểm tra các giá trị băm của phần tử là như nhau và chỉ sau đó so sánh về đẳng thức, do đó, các phần tử, b và c, phải được băm, nếu không, một danh sách so sánh trực tiếp cho đẳng thức:

if a in [b, c]:
    then_do_this()

0

Các ngôn ngữ kiểu APL cho phép bạn so sánh một vô hướng với mỗi phần tử trong một vectơ trong một thao tác. Điều này tạo ra một vectơ Boolean. Ví dụ, tôi muốn quảng bá một cách đáng xấu hổ máy tính apl có tính năng tối thiểu của tôi, inca ( phiên dịch trực tuyến ).

   a<5
5 
   b<4
4 
   c<5
5 
   a=b c
0 1 

Để giảm giá trị này xuống một giá trị duy nhất, chúng tôi có thể thực hiện bao gồm hoặc bằng cách tổng hợp và kiểm tra giá trị khác.

   0!+/a=b c
1 
   c<6
6 
   0!+/a=b c
0

Vì vậy, như các câu trả lời khác nói, vấn đề là cú pháp. Ở một mức độ nào đó, giải pháp cú pháp đã được tìm thấy, với chi phí rất lớn cho việc học mô hình mảng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.