Tại sao 0 sai?


115

Câu hỏi này nghe có vẻ ngu ngốc, nhưng tại sao 0đánh giá falsevà bất kỳ giá trị [số nguyên] nào khác truelà hầu hết các ngôn ngữ lập trình?

So sánh chuỗi

Vì câu hỏi có vẻ hơi đơn giản, tôi sẽ tự giải thích thêm một chút: trước hết, nó có vẻ hiển nhiên đối với bất kỳ lập trình viên nào, nhưng tại sao lại không có ngôn ngữ lập trình - thực sự có thể có, nhưng không phải Tôi đã sử dụng - nơi 0đánh giá truevà tất cả các giá trị [số nguyên] khác false? Đó là một nhận xét có vẻ ngẫu nhiên, nhưng tôi có một vài ví dụ mà nó có thể là một ý tưởng tốt. Trước hết, hãy lấy ví dụ về chuỗi so sánh ba chiều, tôi sẽ lấy C strcmplàm ví dụ: bất kỳ lập trình viên nào thử C làm ngôn ngữ đầu tiên của anh ta có thể bị cám dỗ để viết mã sau đây:

if (strcmp(str1, str2)) { // Do something... }

Vì các strcmptrả về 0đánh giá falsekhi các chuỗi bằng nhau, những gì lập trình viên ban đầu đã cố gắng làm thất bại thảm hại và anh ta thường không hiểu tại sao lúc đầu. Đã được 0đánh giá truethay vào đó, chức năng này có thể đã được sử dụng trong biểu thức đơn giản nhất của nó - hàm trên - khi so sánh về đẳng thức, và kiểm tra thích hợp cho -11sẽ chỉ được thực hiện khi cần. Chúng tôi đã có thể coi loại trả lại là bool(trong suy nghĩ của chúng tôi ý tôi) hầu hết thời gian.

Hơn nữa, chúng ta hãy giới thiệu một loại mới, sign, mà chỉ mất giá trị -1, 01. Điều đó có thể khá tiện dụng. Hãy tưởng tượng có một toán tử tàu vũ trụ trong C ++ và chúng tôi muốn nó std::string(tốt, đã có comparechức năng, nhưng toán tử tàu vũ trụ thú vị hơn). Tuyên bố hiện tại sẽ là một trong những điều sau đây:

sign operator<=>(const std::string& lhs, const std::string& rhs);

Đã 0được đánh giá true, toán tử tàu vũ trụ thậm chí sẽ không tồn tại và chúng ta có thể đã tuyên bố operator==theo cách đó:

sign operator==(const std::string& lhs, const std::string& rhs);

Điều này operator==sẽ xử lý so sánh ba chiều cùng một lúc và vẫn có thể được sử dụng để thực hiện kiểm tra sau trong khi vẫn có thể kiểm tra chuỗi nào vượt trội hơn so với chuỗi khác khi cần:

if (str1 == str2) { // Do something... }

Xử lý lỗi cũ

Bây giờ chúng ta có ngoại lệ, vì vậy phần này chỉ áp dụng cho các ngôn ngữ cũ, nơi không có thứ đó tồn tại (ví dụ C). Nếu chúng ta xem thư viện chuẩn của C (và cả POSIX nữa), chúng ta có thể thấy chắc chắn rằng các hàm maaaaany trở lại 0khi thành công và bất kỳ số nguyên nào khác. Tôi đã buồn khi thấy một số người làm điều này:

#define TRUE 0
// ...
if (some_function() == TRUE)
{
    // Here, TRUE would mean success...
    // Do something
}

Nếu chúng ta nghĩ về cách chúng ta nghĩ trong lập trình, chúng ta thường có mẫu lý luận sau:

Do something
Did it work?
Yes ->
    That's ok, one case to handle
No ->
    Why? Many cases to handle

Nếu chúng ta nghĩ về nó một lần nữa, sẽ có ý nghĩa khi đặt giá trị trung tính duy nhất 0, yes(và đó là cách các chức năng của C hoạt động), trong khi tất cả các giá trị khác có thể ở đó để giải quyết nhiều trường hợp no. Tuy nhiên, trong tất cả các ngôn ngữ lập trình mà tôi biết (ngoại trừ có thể một số ngôn ngữ thực nghiệm thử nghiệm), sẽ yesđánh giá falsetrong một ifđiều kiện, trong khi tất cả các notrường hợp ước tính true. Có nhiều tình huống khi "nó hoạt động" đại diện cho một trường hợp trong khi "nó không hoạt động" đại diện cho nhiều nguyên nhân có thể xảy ra. Nếu chúng ta nghĩ về nó theo cách đó, việc 0đánh giá truevà phần còn lại falsesẽ có ý nghĩa hơn nhiều.

Phần kết luận

Kết luận của tôi về cơ bản là câu hỏi ban đầu của tôi: tại sao chúng tôi thiết kế ngôn ngữ mà 0falsevà các giá trị khác true, dùng trong tài khoản vài ví dụ của tôi ở trên và có thể một số chi tiết tôi không nghĩ ra?

Theo dõi: Thật tuyệt khi thấy có nhiều câu trả lời với nhiều ý tưởng và càng nhiều lý do có thể khiến nó trở nên như vậy. Tôi yêu cách bạn đam mê dường như về nó. Tôi ban đầu hỏi câu hỏi này vì chán, nhưng vì bạn có vẻ rất say mê, tôi quyết định đi xa hơn một chút và hỏi về lý do đằng sau sự lựa chọn Boolean cho 0 và 1 trên Math.SE :)


32
strcmp()không có ví dụ tốt cho đúng hay sai, vì nó trả về 3 giá trị khác nhau. Và bạn sẽ ngạc nhiên khi bạn bắt đầu sử dụng shell, trong đó 0 có nghĩa là đúng và bất cứ điều gì khác có nghĩa là sai.
ott--

52
@ ott--: Trong hệ vỏ Unix, 0 có nghĩa là thành công và khác không có nghĩa là thất bại - không hoàn toàn giống với "đúng" và "sai".
Keith Thompson

14
@KeithThndry: Trong Bash (và các hệ vỏ khác), "thành công" và "thất bại" thực sự giống như "đúng" và "sai". Ví dụ, xem xét câu lệnh if true ; then ... ; fi, trong đó truemột lệnh trả về 0 và điều này cho biết ifđể chạy ....
ruakh

13
Không có booleans trong phần cứng, chỉ có số nhị phân và trong hầu hết các ISAs lịch sử, một số khác không được coi là "đúng" trong tất cả các hướng dẫn phân nhánh có điều kiện (trừ khi chúng sử dụng cờ thay thế). Vì vậy, các ngôn ngữ cấp thấp bằng mọi cách bắt buộc phải tuân theo các thuộc tính phần cứng cơ bản.
SK-logic

2
@MasonWheeler Có loại boolean không ngụ ý gì cả. Ví dụ python không có một boolloại nhưng so sánh / nếu điều kiện vv có thể có bất kỳ giá trị trả về.
Bakuriu

Câu trả lời:


98

0falsebởi vì họ đang cả zero yếu tố chung semirings . Mặc dù chúng là các kiểu dữ liệu riêng biệt, nhưng nó có ý nghĩa trực quan để chuyển đổi giữa chúng vì chúng thuộc các cấu trúc đại số đẳng hình.

  • 0là danh tính cho phép cộng và số 0 cho phép nhân. Điều này đúng với số nguyên và số hữu tỷ, nhưng không đúng với số dấu phẩy động của IEEE-754: 0.0 * NaN = NaN0.0 * Infinity = NaN.

  • falselà danh tính cho Boolean xor (⊻) và 0 cho Boolean (∧). Nếu Booleans được biểu diễn dưới dạng {0, 1} Tập hợp các số nguyên modulo 2 thì bạn có thể nghĩ về ⊻ là phép cộng mà không mang và ∧ là phép nhân.

  • ""[]là bản sắc để nối, nhưng có một số thao tác mà chúng có nghĩa là số không. Sự lặp lại là một, nhưng sự lặp lại và sự kết hợp không phân phối, vì vậy các hoạt động này không tạo thành một nửa.

Chuyển đổi ngầm như vậy rất hữu ích trong các chương trình nhỏ, nhưng lớn có thể làm cho các chương trình khó lý do hơn. Chỉ là một trong nhiều sự đánh đổi trong thiết kế ngôn ngữ.


1
Rất vui khi bạn đề cập đến danh sách. (BTW, nilcó phải là cả danh sách trống []falsegiá trị trong Common Lisp; có xu hướng hợp nhất các danh tính từ các loại dữ liệu khác nhau không?) và không phải là cách khác. Không thể coi truelà xác định cho ANDvà không cho OR?
Giorgio

3
+1 để tham khảo danh tính tương tự. Cuối cùng, một câu trả lời không chỉ là "quy ước, giải quyết nó".
l0b0

5
+1 để cung cấp chi tiết về một toán học cụ thể và rất cũ, trong đó điều này đã được theo dõi và có ý nghĩa từ lâu
Jimmy Hoffa

1
Câu trả lời này không có ý nghĩa. truecũng là danh tính và số không của semirings (Boolean và / hoặc). Không có lý do, quy ước appart, để xem xét falselà gần với 0 hơn true.
TonioElGringo

1
@TonioElGringo: Sự khác biệt giữa đúng và sai là sự khác biệt giữa XOR và XNOR. Người ta có thể tạo các vòng đẳng hình bằng cách sử dụng AND / XOR, trong đó true là nhận dạng nhân và sai là cộng gộp, hoặc với OR và XNOR, trong đó false là danh tính nhân và true là phụ gia, nhưng XNOR thường không được coi là phổ biến hoạt động cơ bản theo cách XOR.
supercat

74

Bởi vì toán học hoạt động.

FALSE OR TRUE is TRUE, because 0 | 1 is 1.

... insert many other examples here.

Theo truyền thống, các chương trình C có điều kiện như

if (someFunctionReturningANumber())

thay vì

if (someFunctionReturningANumber() != 0)

bởi vì khái niệm về số 0 tương đương với sai được hiểu rõ.


21
Các ngôn ngữ được thiết kế như vậy bởi vì toán học có ý nghĩa. Điều đó đã đến đầu tiên.
Robert Harvey

26
@Morwenn, nó quay trở lại thế kỷ 19 và George Boole. Mọi người đã đại diện cho Sai là 0 và Đúng là! 0 lâu hơn so với máy tính.
Charles E. Grant

11
Tôi không thấy lý do tại sao toán học không hoạt động theo cách khác nếu bạn chỉ thay đổi tất cả các định nghĩa sao cho AND là + và OR là *.
Neil G

7
Chính xác: toán học hoạt động cả hai cách và câu trả lời cho câu hỏi này dường như là nó hoàn toàn thông thường.
Neil G

6
@Robert Thật tuyệt nếu bạn có thể đánh vần "nền tảng toán học" trong bài viết của mình.
phant0m

38

Như những người khác đã nói, toán học đến đầu tiên. Đây là lý do tại sao 0 là falsevà 1 là true.

Chúng ta đang nói về toán học nào? Đại số Boolean xuất hiện từ giữa những năm 1800, rất lâu trước khi máy tính kỹ thuật số xuất hiện.

Bạn cũng có thể nói rằng quy ước xuất phát từ logic mệnh đề , thậm chí còn cũ hơn đại số boolean. Đây là sự chính thức hóa của rất nhiều kết quả logic mà các lập trình viên biết và yêu thích ( false || xbằng x, true && xbằng xvà vv).

Về cơ bản chúng ta đang nói về số học trên một tập hợp có hai yếu tố. Hãy suy nghĩ về việc đếm trong nhị phân. Đại số Boolean là nguồn gốc của khái niệm này và nền tảng lý thuyết của nó. Các quy ước của các ngôn ngữ như C chỉ là một ứng dụng đơn giản.


2
Bạn có thể, chắc chắn. Nhưng giữ cho nó theo cách "tiêu chuẩn" phù hợp với số học chung (0 + 1 = 1, không phải 0 + 1 = 0).
joshin4colours

2
Có, nhưng có lẽ bạn sẽ viết AND với + và OR với * nếu bạn cũng đảo ngược các định nghĩa.
Neil G

3
Toán học không đến trước. Toán học nhận ra rằng 0 và 1 tạo thành một trường, trong đó AND giống như phép nhân và OR giống như phép cộng.
Kaz

1
@Kaz: Nhưng {0, 1} với OR và AND không tạo thành một trường.
Giorgio

2
Nó làm phiền tôi một chút rằng nhiều câu trả lời và ý kiến ​​nói rằng true = 1. Điều đó không hoàn toàn chính xác, bởi vì true != 0nó không hoàn toàn giống nhau. Một lý do (không phải là duy nhất) tại sao người ta nên tránh so sánh như thế nào if(something == true) { ... }.
JensG

26

Tôi nghĩ rằng điều này có liên quan đến "sự kế thừa" từ thiết bị điện tử, và cả đại số boolean, nơi

  • 0= off, negative, no,false
  • 1= on, positive, yes,true

strcmp trả về 0 khi các chuỗi bằng nhau có liên quan đến việc thực hiện nó, vì những gì nó thực sự làm là tính "khoảng cách" giữa hai chuỗi. Con số 0 đó cũng được coi là sai chỉ là sự trùng hợp.

Trả về 0 khi thành công có ý nghĩa vì 0 trong trường hợp này được sử dụng có nghĩa là không có lỗi và bất kỳ số nào khác sẽ là mã lỗi. Sử dụng bất kỳ số nào khác để thành công sẽ có ý nghĩa ít hơn vì bạn chỉ có một mã thành công duy nhất, trong khi bạn có thể có một số mã lỗi. Bạn sử dụng "Nó có hoạt động không?" như biểu thức câu lệnh if và nói 0 = yes sẽ có ý nghĩa hơn, nhưng biểu thức chính xác hơn "Có gì sai không?" và sau đó bạn thấy rằng 0 = không có nhiều ý nghĩa. Suy nghĩ về false/trueviệc không thực sự có ý nghĩa ở đây, vì nó thực sự no error code/error code.


Haha, bạn là người đầu tiên nêu rõ câu hỏi lỗi trả về. Tôi đã biết tôi đã giải thích nó theo cách riêng của mình và nó có thể bằng cách hỏi theo cách khác, nhưng bạn là người đầu tiên thể hiện nó một cách rõ ràng (trong số nhiều câu trả lời và nhận xét). Trên thực tế, tôi sẽ không nói rằng cách này hay cách khác không có ý nghĩa, nhưng hơn nữa cả hai đều có ý nghĩa theo những cách khác nhau :)
Morwenn

1
Thật sự tôi muốn nói 0cho success/no errorlà điều duy nhất có ý nghĩa khi số nguyên khác đại diện cho mã lỗi. Điều đó 0cũng xảy ra để đại diện falsetrong các trường hợp khác không thực sự quan trọng, vì chúng ta không nói về đúng hay sai ở đây;)
Svish

Tôi đã có cùng một ý tưởng vì vậy tôi đã tăng
user60812

1
Quan điểm của bạn về strcmp()tính toán khoảng cách là khá tốt. Nếu nó đã được gọi strdiff()thì if (!strdiff())sẽ rất logic.
Kevin Cox

"Điện tử [...] trong đó 0 = [...] sai, 1 = [...] đúng" - ngay cả trong điện tử, đây chỉ là quy ước và không phải là duy nhất. Chúng tôi gọi logic tích cực này, nhưng bạn cũng có thể sử dụng logic âm, trong đó điện áp dương biểu thị sai và âm cho biết đúng. Sau đó, mạch bạn sử dụng cho AND trở thành OR, OR trở thành AND, v.v. Theo luật của De Morgan, tất cả cuối cùng đều tương đương. Đôi khi, bạn sẽ tìm thấy một phần của mạch điện tử được triển khai theo logic tiêu cực để thuận tiện, tại đó, tên của các tín hiệu trong phần đó được ghi chú bằng một thanh phía trên chúng.
Jules

18

Như đã giải thích trong bài viết này , các giá trị falsetruekhông nên nhầm lẫn với các số nguyên 0 và 1, nhưng có thể được xác định bằng các phần tử của trường Galois (trường hữu hạn) của hai phần tử (xem tại đây ).

Một trường là một tập hợp với hai hoạt động thỏa mãn các tiên đề nhất định.

Các ký hiệu 0 và 1 thường được sử dụng để biểu thị các danh tính cộng và nhân của một trường bởi vì các số thực cũng là một trường (nhưng không phải là hữu hạn) có danh tính là các số 0 và 1.

Nhận dạng phụ gia là thành phần 0 của trường, sao cho tất cả x:

x + 0 = 0 + x = x

và danh tính nhân là phần tử 1 của trường, sao cho tất cả x:

x * 1 = 1 * x = x

Trường hữu hạn của hai phần tử chỉ có hai phần tử này, đó là danh tính cộng 0 (hoặc false) và danh tính nhân 1 (hoặc true). Hai hoạt động của trường này là XOR logic (+) và logic AND (*).

Ghi chú. Nếu bạn lật các phép toán (XOR là phép nhân và AND là phép cộng) thì phép nhân không phân phối so với phép cộng và bạn không còn trường nào nữa. Trong trường hợp như vậy, bạn không có lý do để gọi hai phần tử 0 và 1 (theo bất kỳ thứ tự nào). Cũng lưu ý rằng bạn không thể chọn thao tác HOẶC thay vì XOR: cho dù bạn diễn giải OR / AND như thế nào khi cộng / nhân, cấu trúc kết quả không phải là một trường (không phải tất cả các phần tử nghịch đảo đều tồn tại theo yêu cầu của các tiên đề trường).

Về các chức năng C:

  • Nhiều hàm trả về một số nguyên là mã lỗi. 0 có nghĩa là KHÔNG CÓ L ERI.
  • Theo trực giác, hàm strcmptính toán sự khác biệt giữa hai chuỗi. 0 có nghĩa là không có sự khác biệt giữa hai chuỗi, tức là hai chuỗi bằng nhau.

Các giải thích trực quan ở trên có thể giúp ghi nhớ việc giải thích các giá trị trả về, nhưng thậm chí còn dễ dàng hơn khi chỉ kiểm tra tài liệu thư viện.


1
+1 để chỉ ra rằng nếu bạn tự ý trao đổi những thứ này, toán học không còn hoạt động nữa.
Jimmy Hoffa

2
Lật: Đưa ra một trường có hai phần tử và hoạt động * và +, chúng tôi xác định Đúng với 0 và Sai với 1. Chúng tôi xác định OR với * và XOR với +.
Neil G

1
Bạn sẽ thấy rằng cả hai nhận dạng này đều được thực hiện trên cùng một trường và cả hai đều phù hợp với các quy tắc của logic Boolean. Rất tiếc, ghi chú của bạn không chính xác :)
Neil G

1
Nếu bạn giả sử rằng True = 0 và XOR là +, thì True phải là danh tính cho XOR. Nhưng đó không phải là vì True XOR Đúng = Sai. Trừ khi bạn xác định lại XOR hoạt động trên True để True XOR True = True. Sau đó, tất nhiên công trình xây dựng của bạn hoạt động vì bạn vừa đổi tên mọi thứ (trong bất kỳ cấu trúc toán học nào, bạn luôn có thể thực hiện thành công hoán vị tên và có được cấu trúc đẳng cấu). Mặt khác, nếu bạn để True, false và XOR có nghĩa thông thường, thì True XOR True = false và True không thể là danh tính phụ gia, tức là True không thể là 0.
Giorgio

1
@Giorgio: Tôi đã sửa chữa công trình của tôi theo nhận xét của bạn trong bình luận cuối cùng của tôi
Neil G

13

Bạn nên xem xét rằng các hệ thống thay thế cũng có thể là quyết định thiết kế được chấp nhận.

Shell: 0 trạng thái thoát là đúng, khác không là sai

Ví dụ về các shell xử lý trạng thái thoát 0 là true đã được đề cập.

$ ( exit 0 ) && echo "0 is true" || echo "0 is false"
0 is true
$ ( exit 1 ) && echo "1 is true" || echo "1 is false"
1 is false

Lý do là có một cách để thành công, nhưng có nhiều cách để thất bại, vì vậy sử dụng 0 làm giá trị đặc biệt có nghĩa là "không có lỗi" là thực dụng.

Ruby: 0 giống như bất kỳ số nào khác

Trong số các ngôn ngữ lập trình "bình thường", có một số ngoại lệ, chẳng hạn như Ruby, coi 0 là giá trị thực.

$ irb
irb(main):001:0> 0 ? '0 is true' : '0 is false'
=> "0 is true"

Các lý do là chỉ falsenilphải là sai lầm. Đối với nhiều người mới chơi Ruby, đó là một gotcha. Tuy nhiên, trong một số trường hợp, thật tuyệt khi 0 được đối xử giống như bất kỳ số nào khác.

irb(main):002:0> (pos = 'axe' =~ /x/) ? "Found x at position #{pos}" : "x not found"
=> "Found x at position 1"
irb(main):003:0> (pos = 'xyz' =~ /x/) ? "Found x at position #{pos}" : "x not found"
=> "Found x at position 0"
irb(main):004:0> (pos = 'abc' =~ /x/) ? "Found x at position #{pos}" : "x not found"
=> "x not found"

Tuy nhiên, một hệ thống như vậy chỉ hoạt động trong một ngôn ngữ có thể phân biệt booleans là một loại riêng biệt với các số. Trong những ngày đầu của máy tính, các lập trình viên làm việc với ngôn ngữ lắp ráp hoặc ngôn ngữ máy thô không có những thứ xa xỉ như vậy. Có lẽ chỉ là tự nhiên khi coi 0 là trạng thái "trống" và đặt bit thành 1 làm cờ khi mã phát hiện điều gì đó đã xảy ra. Bằng cách mở rộng, quy ước đã phát triển rằng số 0 được coi là sai và các giá trị khác không được coi là đúng. Tuy nhiên, nó không phải theo cách đó.

Java: Các số không thể được coi là booleans

Trong Java truefalselà các giá trị boolean duy nhất. Các số không phải là booleans và thậm chí không thể chuyển thành booleans ( Đặc tả ngôn ngữ Java, Sec 4.2.2 ):

Không có phôi giữa các loại tích phân và loại boolean.

Quy tắc đó chỉ tránh hoàn toàn câu hỏi - tất cả các biểu thức boolean phải được viết rõ ràng trong mã.


1
Rebol và Red đều đối xử với INTEGER có giá trị 0! các giá trị là đúng và có một KHÔNG riêng! loại (chỉ có một giá trị, KHÔNG) được coi là sai có điều kiện ngoài LOGIC! sai trái. Tôi đã thấy sự thất vọng đáng kể khi cố gắng viết mã JavaScript coi 0 là sai; đó là một quyết định cực kỳ khó hiểu đối với một ngôn ngữ được gõ động. Nếu bạn muốn kiểm tra một cái gì đó có thể là null hoặc 0 bạn sẽ phải viết if (thing === 0), điều đó thật tuyệt.
HostileFork

@HostileFork Tôi không biết. Tôi thấy rằng nó có ý nghĩa rằng 0true(như mọi số nguyên khác) bằng một ngôn ngữ năng động. Đôi khi tôi tình cờ bắt 0khi cố gắng bắt Nonebằng Python, và đôi khi có thể khá khó khăn để phát hiện.
Morwenn

2
Ruby không phải là một ngoại lệ. Ruby lấy thứ này từ Lisp (Ruby thậm chí còn được gọi bí mật là "MatzLisp"). Lisp là một ngôn ngữ chính trong khoa học máy tính. Số không cũng chỉ là một giá trị thực trong vỏ POSIX, vì đó là một đoạn văn bản : if [ 0 ] ; then echo this executes ; fi. Giá trị dữ liệu sai là một chuỗi rỗng và sai có thể kiểm tra là trạng thái chấm dứt không thành công của một lệnh, được biểu thị bằng một số không khác.
Kaz

8

Trước khi giải quyết trường hợp chung, chúng tôi có thể thảo luận về các ví dụ truy cập của bạn.

So sánh chuỗi

Điều tương tự giữ cho nhiều loại so sánh, thực sự. So sánh như vậy tính khoảng cách giữa hai đối tượng. Khi các đối tượng bằng nhau, khoảng cách là tối thiểu. Vì vậy, khi "so sánh thành công", giá trị là 0. Nhưng thực sự, giá trị trả về strcmpkhông một boolean, đó là một khoảng cách, và rằng những gì bẫy các lập trình viên không biết làm if (strcmp(...)) do_when_equal() else do_when_not_equal().

Trong C ++, chúng ta có thể thiết kế lại strcmpđể trả về một Distanceđối tượng, ghi đè operator bool()trở lại đúng khi 0 (nhưng sau đó bạn sẽ bị cắn bởi một nhóm vấn đề khác). Hoặc trong C đơn giản chỉ có một streqhàm trả về 1 khi các chuỗi bằng nhau và 0 khác.

Cuộc gọi API / mã thoát chương trình

Ở đây bạn quan tâm đến lý do một cái gì đó đã đi sai, bởi vì điều này sẽ thúc đẩy các quyết định lên lỗi. Khi mọi thứ thành công, bạn không muốn biết bất cứ điều gì cụ thể - ý định của bạn được thực hiện. Giá trị trả lại do đó phải truyền đạt thông tin này. Nó không phải là một boolean, nó là một mã lỗi. Giá trị lỗi đặc biệt 0 có nghĩa là "không có lỗi". Phần còn lại của phạm vi biểu thị các lỗi có ý nghĩa cục bộ mà bạn phải xử lý (bao gồm 1, thường có nghĩa là "lỗi không xác định").

Trường hợp chung

Điều này cho chúng ta câu hỏi: tại sao các giá trị boolean TrueFalsethường được biểu thị bằng 1 và 0 tương ứng?

Chà, bên cạnh lập luận chủ quan "nó cảm thấy tốt hơn theo cách này", đây là một vài lý do (chủ quan là tốt) tôi có thể nghĩ ra:

  • tương tự mạch điện. Hiện tại là BẬT trong 1 giây và TẮT trong 0 giây. Tôi thích có (1, Có, Đúng, Bật) cùng nhau và (0, Không, Sai, Tắt), thay vì kết hợp khác

  • khởi tạo bộ nhớ. Khi tôi có memset(0)một loạt các biến (có thể là ints, float, bools), tôi muốn giá trị của chúng khớp với các giả định bảo thủ nhất. Ví dụ: tổng của tôi ban đầu là 0, vị ngữ là Sai, v.v.

Có lẽ tất cả những lý do này gắn liền với giáo dục của tôi - nếu tôi đã được dạy để liên kết 0 với True ngay từ đầu, tôi sẽ đi theo con đường khác.


2
Trên thực tế có ít nhất một ngôn ngữ lập trình coi 0 là đúng. Vỏ unix.
Jan Hudec

+1 để giải quyết vấn đề thực sự: Hầu hết câu hỏi của Morwenn hoàn toàn không phải là về bool.
dan04

@ dan04 Đó là. Toàn bộ bài là về lý do đằng sau sự lựa chọn của dàn diễn viên từ intđến booltrong nhiều ngôn ngữ lập trình. Các công cụ so sánh và lỗi lỗi chỉ là ví dụ về các địa điểm mà việc truyền nó theo cách khác so với công cụ hiện tại được thực hiện sẽ có ý nghĩa.
Morwenn

6

Từ góc độ cấp cao, bạn đang nói về ba loại dữ liệu khá khác nhau:

  1. Một boolean. Quy ước toán học trong đại số Boolean là sử dụng 0 cho falsevà 1 cho true, vì vậy sẽ rất hợp lý khi tuân theo quy ước đó. Tôi nghĩ rằng cách này cũng có ý nghĩa hơn bằng trực giác.

  2. Kết quả so sánh. Đây có ba giá trị: <, =>(lưu ý rằng không ai trong số họ là true). Đối với họ, việc sử dụng các giá trị -1, 0 và 1 tương ứng là hợp lý (hoặc nói chung hơn là giá trị âm, số 0 và giá trị dương).

    Nếu bạn muốn kiểm tra sự bình đẳng và bạn chỉ có một chức năng thực hiện so sánh chung, tôi nghĩ bạn nên làm cho nó rõ ràng bằng cách sử dụng một cái gì đó như strcmp(str1, str2) == 0. Tôi thấy việc sử dụng !trong tình huống này là khó hiểu, bởi vì nó xử lý một giá trị không phải là boolean như thể nó là một boolean.

    Ngoài ra, hãy nhớ rằng so sánh và bình đẳng không phải là điều tương tự. Ví dụ, nếu bạn ra lệnh cho mọi người theo ngày sinh của họ, Compare(me, myTwin)nên trả lại 0, nhưng Equals(me, myTwin)nên trả lại false.

  3. Thành công hay thất bại của một chức năng, cũng có thể với các chi tiết về thành công hay thất bại đó. Nếu bạn đang nói về Windows, thì loại này được gọi HRESULTvà giá trị khác không nhất thiết không phải là lỗi. Trong thực tế, một giá trị tiêu cực cho thấy thất bại và thành công không tiêu cực. Giá trị thành công là rất thường xuyên S_OK = 0, nhưng nó cũng có thể là ví dụ S_FALSE = 1, hoặc các giá trị khác.

Sự nhầm lẫn xuất phát từ thực tế là ba loại dữ liệu khá khác nhau về mặt logic thực sự được biểu diễn dưới dạng một loại dữ liệu (một số nguyên) trong C và một số ngôn ngữ khác và bạn có thể sử dụng số nguyên trong một điều kiện. Nhưng tôi không nghĩ sẽ hợp lý khi xác định lại boolean để làm cho việc sử dụng một số loại không phải boolean trong điều kiện đơn giản hơn.

Ngoài ra, hãy xem xét một loại khác thường được sử dụng trong một điều kiện trong C: một con trỏ. Ở đó, thật tự nhiên khi coi một con NULLtrỏ (được biểu thị là 0) như false. Vì vậy, làm theo đề xuất của bạn cũng sẽ làm cho việc làm việc với con trỏ trở nên khó khăn hơn. (Mặc dù, về mặt cá nhân, tôi thích so sánh rõ ràng với các con trỏ với NULL, thay vì coi chúng là booleans.)


4

Không có thể sai vì hầu hết các CPU đều có cờ ZERO có thể được sử dụng để phân nhánh. Nó tiết kiệm một hoạt động so sánh.

Hãy xem tại sao.

Một số psuedocode, vì khán giả có thể không đọc lắp ráp

c- nguồn cuộc gọi vòng lặp đơn giản 10 lần

for (int foo =10; foo>0; foo-- ) /* down count loop is shorter */
{  
   wibble();
}

một số lắp ráp giả vờ cho rằng

0x1000 ld a 0x0a      'foo=10
0x1002 call 0x1234    'call wibble()
0x1005 dec a          'foo--
0x1006 jrnz -0x06      'jump back to 0x1000 if not zero
0x1008  

c- nguồn một cuộc gọi vòng lặp đơn giản khác gấp 10 lần

for (int foo =0; foo<10; foo-- ) /* up count loop is longer  */
{  
   wibble();
}

một số lắp ráp giả vờ cho trường hợp này

0x1000 ld a 0x00      'foo=0
0x1002 call 0x1234    'call wibble()
0x1005 dec a          'foo--
0x1006 cmp 0x0a       'compare foo to 10 ( like a subtract but we throw the result away)
0x1008 jrns -0x08      'jump back to 0x1000 if compare was negative
0x100a  

thêm một số nguồn c

int foo=10;
if ( foo ) wibble()

và lắp ráp

0x1000 ld a 0x10
0x1002 jz 0x3
0x1004 call 0x1234
0x1007  

xem nó ngắn thế nào?

thêm một số nguồn c

int foo=10;
if ( foo==0 ) wibble()

và lắp ráp (giả sử một trình biên dịch thông minh cận biên có thể thay thế == 0 mà không cần so sánh)

0x1000 ld a 0x10
0x1002 jz 0x3
0x1004 call 0x1234
0x1007  

Bây giờ hãy thử một quy ước đúng = 1

thêm một số nguồn c #define TRUE 1 int foo = TRUE; if (foo == TRUE) wibble ()

và lắp ráp

0x1000 ld a 0x1
0x1002 cmp a 0x01
0x1004 jz 0x3
0x1006 call 0x1234
0x1009 

xem trường hợp ngắn với nonzero đúng là như thế nào?

CPU thực sự ban đầu có các bộ cờ nhỏ được gắn vào Accumulator.

Để kiểm tra xem a> b hay a = b thường có hướng dẫn so sánh.

  • Trừ khi B là ZERO - trong trường hợp đó, cờ ZERO được đặt Được triển khai dưới dạng một logic logic đơn giản hoặc tất cả các bit trong Bộ tích lũy.
  • Hoặc NEGECT trong đó chỉ sử dụng "bit dấu" tức là bit có ý nghĩa nhất của Tích lũy nếu bạn đang sử dụng số học bổ sung của hai. (Chủ yếu là chúng tôi làm)

Hãy nói lại điều này. Trên một số CPU cũ hơn, bạn không phải sử dụng hướng dẫn so sánh cho bộ tích lũy bằng ZERO hoặc bộ tích lũy nhỏ hơn 0.

Bây giờ bạn có thấy tại sao số không có thể là sai?

Xin lưu ý đây là mã psuedo và không có tập lệnh thực sự nào trông giống như thế này. Nếu bạn biết lắp ráp, bạn biết tôi đang đơn giản hóa mọi thứ ở đây. Nếu bạn biết bất cứ điều gì về thiết kế trình biên dịch, bạn không cần phải đọc câu trả lời này. Bất cứ ai biết bất cứ điều gì về dự đoán vòng lặp hoặc dự đoán chi nhánh, lớp nâng cao đều xuống sảnh trong phòng 203.


2
Quan điểm của bạn không được thực hiện tốt ở đây vì một điều if (foo)if (foo != 0)sẽ tạo ra cùng một mã, và thứ hai, bạn đang cho thấy rằng ngôn ngữ lắp ráp mà bạn đang sử dụng trên thực tế có các toán hạng boolean rõ ràng và kiểm tra chúng. Ví dụ jzcó nghĩa là jump if zero. Nói cách khác if (a == 0) goto target;. Và số lượng thậm chí không được kiểm tra trực tiếp; điều kiện được chuyển đổi thành cờ boolean được lưu trữ trong một từ máy đặc biệt. Nó thực sự giống nhưcpu.flags.zero = (a == 0); if (cpu.flags.zero) goto target;
Kaz

Không có Kaz, CPU cũ không hoạt động như vậy. Jz / jnz có thể được thực hiện mà không cần thực hiện một hướng dẫn so sánh. Đó là loại quan điểm của toàn bộ bài viết của tôi thực sự.
Tim Williscroft

2
Tôi đã không viết bất cứ điều gì về một hướng dẫn so sánh.
Kaz

Bạn có thể trích dẫn một bộ xử lý có jzhướng dẫn nhưng không jnz? (hoặc bất kỳ bộ hướng dẫn điều kiện bất đối xứng nào khác)
Toby Speight

3

Có rất nhiều câu trả lời cho thấy rằng sự tương ứng giữa 1 và true là bắt buộc bởi một số thuộc tính toán học. Tôi không thể tìm thấy bất kỳ tài sản như vậy và đề nghị nó hoàn toàn là quy ước lịch sử.

Cho một trường có hai phần tử, chúng ta có hai phép toán: phép cộng và phép nhân. Chúng tôi có thể ánh xạ các hoạt động Boolean trên trường này theo hai cách:

Theo truyền thống, chúng tôi xác định Đúng với 1 và Sai với 0. Chúng tôi xác định AND với * và XOR với +. Do đó OR là bão hòa bổ sung.

Tuy nhiên, chúng ta có thể dễ dàng xác định Đúng bằng 0 và Sai bằng 1. Sau đó, chúng tôi xác định HOẶC với * và XNOR với +. Do đó, AND đang bão hòa.


4
Nếu bạn đã theo liên kết trên wikipedia, bạn có thể thấy rằng khái niệm đại số boolean được đóng lại liên quan đến trường Galois gồm hai phần tử ( en.wikipedia.org/wiki/GF%282%29 ). Các ký hiệu 0 và 1 thường được sử dụng để biểu thị các danh tính cộng và nhân, tương ứng, bởi vì các số thực cũng là một trường có danh tính là các số 0 và 1.
Giorgio

1
@NeilG Tôi nghĩ Giorgio đang cố nói điều đó không chỉ là một quy ước. 0 và 1 trong đại số boolean về cơ bản giống như 0 và 1 trong GF (2), hoạt động gần giống như 0 và 1 trong các số thực liên quan đến phép cộng và phép nhân.
Svick

1
@svick: Không, vì bạn có thể chỉ cần đổi tên phép nhân và phép cộng bão hòa thành OR và AND và sau đó lật nhãn sao cho 0 là Đúng và 1 là Sai. Giorgio đang nói rằng đó là một quy ước của logic Boolean, được sử dụng như một quy ước của khoa học máy tính.
Neil G

1
@Neil G: Không, bạn không thể lật + và * và 0 và 1 vì một trường yêu cầu phân phối phép nhân so với phép cộng (xem en.wikipedia.org/wiki/Field_%28mathatures%29 ), nhưng nếu bạn đặt +: = AND và *: = XOR, bạn nhận được T XOR (T VÀ F) = T XOR F = T, trong khi (T XOR T) AND (T XOR F) = F VÀ T = F. Do đó bằng cách lật các thao tác và danh tính bạn không có một lĩnh vực nào nữa. Vì vậy, IMO định nghĩa 0 và 1 là danh tính của một trường thích hợp dường như nắm bắt sai và đúng khá trung thực.
Giorgio

1
@giorgio: Tôi đã chỉnh sửa câu trả lời để làm rõ những gì đang diễn ra.
Neil G

3

Kỳ lạ thay, không phải lúc nào cũng sai.

Cụ thể, quy ước Unix và Posix là định nghĩa EXIT_SUCCESSlà 0 (và EXIT_FAILURElà 1). Trên thực tế, nó thậm chí là một quy ước C tiêu chuẩn !

Vì vậy, đối với các vỏ Posix và lối ra (2) tòa nhà, 0 có nghĩa là "thành công", theo trực giác là đúng hơn sai.

Cụ thể, shell ifmuốn có một quá trình trả về EXIT_SUCCESS(đó là 0) để đi theo nhánh "rồi" của nó!

Trong Lược đồ (nhưng không phải trong Lisp chung hoặc trong MELT ) 0 và nil (tức là ()trong Lược đồ) là đúng, vì giá trị sai duy nhất là#f

Tôi đồng ý, tôi đang nitpicking!


3

C được sử dụng để lập trình cấp thấp gần với phần cứng, một lĩnh vực mà đôi khi bạn cần phải thay đổi giữa các hoạt động bitwise và logic, trên cùng một dữ liệu. Được yêu cầu chuyển đổi một biểu thức số thành boolean chỉ để thực hiện kiểm tra sẽ làm lộn xộn mã.

Bạn có thể viết những thứ như:

if (modemctrl & MCTRL_CD) {
   /* carrier detect is on */
}

thay vì

if ((modemctrl & MCTRL_CD) != 0) {
    /* carrier detect is on */
}

Trong một ví dụ riêng biệt, nó không quá tệ, nhưng phải làm điều đó sẽ trở nên khó chịu.

Tương tự như vậy, hoạt động ngược. Nó rất hữu ích cho kết quả của một phép toán boolean, như so sánh, chỉ tạo ra 0 hoặc 1: Giả sử chúng ta muốn đặt bit thứ ba của một số từ dựa trên việc modemctrlcó bit phát hiện sóng mang hay không:

flags |= ((modemctrl & MCTRL_CD) != 0) << 2;

Ở đây chúng ta phải có != 0, để giảm kết quả của &biểu thức biwise thành 0hoặc 1, nhưng vì kết quả chỉ là một số nguyên, chúng ta không cần phải thêm một số cast gây phiền nhiễu để chuyển đổi boolean thành số nguyên.

Mặc dù C hiện đại có một boolloại, nhưng nó vẫn giữ được tính hợp lệ của mã như thế này, bởi vì đó là một điều tốt và vì sự phá vỡ lớn với khả năng tương thích ngược sẽ gây ra nếu không.

Một ví dụ khác trong đó C là khéo léo: kiểm tra hai điều kiện boolean như một công tắc bốn chiều:

switch (foo << 1 | bar) {  /* foo and bar booleans are 0 or 1 */
case 0: /* !foo && !bar */
   break;
case 1: /* !foo && bar */
   break;
case 2: /* foo && !bar */
   break;
case 3: /* foo && bar */
   break;
}

Bạn không thể lấy điều này khỏi lập trình viên C mà không có một cuộc chiến!

Cuối cùng, C đôi khi phục vụ như một loại ngôn ngữ lắp ráp cấp cao. Trong các ngôn ngữ lắp ráp, chúng tôi cũng không có các kiểu boolean. Giá trị boolean chỉ là một bit hoặc 0 so với giá trị khác 0 trong một vị trí bộ nhớ hoặc thanh ghi. Một số nguyên 0, boolean zero và địa chỉ 0 đều được kiểm tra theo cùng một cách trong các tập lệnh ngôn ngữ lắp ráp (và có lẽ cả dấu phẩy động số 0). Sự tương đồng giữa C và ngôn ngữ hợp ngữ là hữu ích, ví dụ khi C được sử dụng làm ngôn ngữ đích để biên dịch một ngôn ngữ khác (ngay cả một ngôn ngữ đã gõ mạnh các booleans!)


0

Giá trị boolean hoặc true chỉ có 2 giá trị. Đúng và sai.

Chúng không nên được biểu diễn dưới dạng số nguyên, mà là bit (0 và 1).

Nói bất kỳ số nguyên nào khác bên cạnh 0 hoặc 1 không sai là một câu khó hiểu. Bảng chân lý đối phó với các giá trị thật, không phải số nguyên.

Từ một giá trị thật có triển vọng, -1 hoặc 2 sẽ phá vỡ tất cả các bảng chân lý và bất kỳ logic boolean nào liên quan đến chúng.

  • 0 VÀ -1 ==?!
  • 0 HOẶC 2 ==?!

Hầu hết các ngôn ngữ thường có một booleanloại mà khi truyền sang một loại số, chẳng hạn như số nguyên sẽ hiển thị sai thành giá trị nguyên là 0.


1
0 VÀ -1 == bất cứ giá trị boolean nào bạn đặt chúng vào. Đó là những gì câu hỏi của tôi là về, tại sao đưa chúng đến TRUEhoặc FALSE. Tôi chưa bao giờ nói - có thể tôi đã làm, nhưng nó không có ý định - số nguyên là đúng hay sai, tôi đã hỏi về lý do tại sao họ đánh giá bất cứ khi nào được chọn vào boolean.
Morwenn

-6

Cuối cùng, bạn đang nói về việc phá vỡ ngôn ngữ cốt lõi bởi vì một số API rất tệ. API crappy không phải là mới và bạn không thể sửa chúng bằng cách phá vỡ ngôn ngữ. Có một thực tế toán học là 0 là sai và 1 là đúng và bất kỳ ngôn ngữ nào không tôn trọng điều này về cơ bản đều bị phá vỡ. So sánh ba chiều là thích hợp và không có doanh nghiệp nào có kết quả hoàn toàn chuyển đổi thành boolvì nó trả về ba kết quả có thể. Các API C cũ đơn giản là có khả năng xử lý lỗi khủng khiếp và cũng bị cản trở vì C không có các tính năng ngôn ngữ cần thiết để không có giao diện khủng.

Lưu ý rằng tôi không nói rằng đối với các ngôn ngữ không có số nguyên ẩn-> chuyển đổi boolean.


11
"Có một thực tế toán học là 0 là sai và 1 là đúng" Erm.
R. Martinho Fernandes

11
Bạn có thể trích dẫn một tài liệu tham khảo cho "thực tế toán học của bạn rằng 0 là sai và 1 là đúng" không? Câu trả lời của bạn nghe có vẻ nguy hiểm như một lời nói.
Dan Pichelman

14
Đó không phải là một thực tế toán học, nhưng nó là một quy ước toán học từ thế kỷ 19.
Charles E. Grant

1
Đại số Boolean được biểu diễn bằng một trường hữu hạn trong đó 0 và 1 là các phần tử nhận dạng cho các hoạt động giống với phép cộng và phép nhân. Các hoạt động đó, tương ứng, HOẶC và AND. Trong thực tế, đại số boolean được viết rất giống đại số bình thường trong đó juxtap vị trí biểu thị AND và +ký hiệu biểu thị OR. Vì vậy, ví dụ abc + a'b'ccó nghĩa là (a and b and c) or (a and (not b) and (not c)).
Kaz
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.