Tại sao dấu chấm phẩy và dấu phẩy được hoán đổi cho các vòng lặp?


49

Trong nhiều ngôn ngữ (một danh sách rộng, từ C đến JavaScript):

  • dấu phẩy ,các đối số riêng biệt (ví dụ func(a, b, c)), trong khi
  • dấu chấm phẩy ;riêng biệt hướng dẫn tuần tự (ví dụ instruction1; instruction2; instruction3).

Vậy tại sao ánh xạ này lại bị đảo ngược trong cùng các ngôn ngữ cho các vòng lặp :

for ( init1, init2; condition; inc1, inc2 )
{
    instruction1;
    instruction2;
}

thay vì (những gì có vẻ tự nhiên hơn đối với tôi)

for ( init1; init2, condition, inc1; inc2 )
{
    instruction1;
    instruction2;
}

?

Chắc chắn, forlà (thường) không phải là một chức năng, nhưng đối số (ví dụ init, condition, increment) cư xử giống như đối số của một hàm hơn một chuỗi các hướng dẫn.

Là do lý do lịch sử / một quy ước, hoặc có một lý do tốt cho việc trao đổi ,;trong các vòng lặp?


1
(Bài đăng thứ 1 của tôi ở đây. Tôi không chắc câu hỏi này thuộc về lập trình viên hay SO nhiều hơn, vì vậy hãy thoải mái di chuyển, nếu cần.)
Piotr Migdal

8
Đây chắc chắn là một bài lập trình viên. Chào mừng bạn :-)
Martijn Pieters

1
"Tại sao không" sẽ cung cấp câu trả lời - bằng cách có cùng một câu trả lời - "Bởi vì ai đó cần đưa ra lựa chọn và đó là lựa chọn họ đưa ra" Giống như "Tại sao họ chọn" {"và"} "và 1000 lựa chọn khác họ đã thực hiện - "Bởi vì".
mattnz

2
@mattnz Câu hỏi là về tính nhất quán (không phải "Tại sao chúng ta ;không sử dụng |?" (hoặc Tại sao chúng ta sử dụng ' other' not 'other '? )) không phải là một ngôn ngữ, mà là một số lượng lớn trong số đó. Một câu trả lời, ví dụ: "nó được tạo ra trong C như một cách viết tắt cho vòng lặp while (và nhiều câu lệnh cho inc chỉ được nghĩ sau này), và mọi người không muốn thay đổi nó để tránh sự khó chịu của lập trình viên" sẽ hoàn toàn ổn.
Piotr Migdal

Tôi nhớ lại việc đọc - có lẽ trong K & R - rằng toán tử dấu phẩy ban đầu được thêm vào ngôn ngữ để có thể khởi tạo nhiều hơn một biến trong biểu thức init của câu lệnh for.
zwol

Câu trả lời:


18

Vậy tại sao trong cùng các ngôn ngữ, ánh xạ như vậy lại bị đảo ngược đối với các vòng lặp.

Về mặt kỹ thuật, ánh xạ không bị "đảo ngược".

  • Những thứ được phân tách bằng dấu phẩy không phải là tham số. Trong (ít nhất) C ++ và Java, chúng có thể là các khai báo, vì vậy chúng thậm chí không phải là biểu thức.
  • Những thứ được phân tách bằng dấu chấm phẩy cũng không phải là câu lệnh (đơn).

Trong thực tế những gì chúng ta có ở đây là một bối cảnh cú pháp khác nhau, nơi các biểu tượng tương tự đang được sử dụng khác nhau. Chúng tôi không so sánh giống như với thích, vì vậy không có ánh xạ và không có đối số mạnh mẽ cho ánh xạ nhất quán dựa trên tính nhất quán ngữ nghĩa.

Vậy tại sao không làm theo cách khác?

Vâng, tôi nghĩ rằng những lý do đến từ ý nghĩa "tự nhiên" của ,;. Trong ngôn ngữ viết tiếng Anh, dấu chấm phẩy "phá vỡ" mạnh hơn dấu phẩy và glyph cho dấu chấm phẩy có thể nhìn thấy rõ hơn dấu phẩy. Hai điều đó kết hợp để làm cho sự sắp xếp hiện tại dường như (với tôi!) Trở nên tự nhiên hơn.

Nhưng cách duy nhất để biết chắc chắn tại sao lựa chọn cú pháp được đưa ra là nếu các nhà thiết kế C có thể cho chúng tôi biết họ đã nghĩ gì vào năm 1970. Tôi nghi ngờ rằng họ có một trí nhớ rõ ràng về các quyết định kỹ thuật được thực hiện từ thời xa xưa.


Có phải vì lý do lịch sử / một quy ước

Tôi không biết bất kỳ ngôn ngữ nào trước C đã sử dụng cú pháp giống C cho các vòng lặp "cho":

  • Donal Fellows lưu ý rằng BCPL và B không có cấu trúc tương đương.

  • Các tương đương FORTRAN, COBOL và Algol-60 (và Pascal) ít biểu cảm hơn và có các cú pháp không giống với cú pháp C "for".

Nhưng các ngôn ngữ như C, C ++ và Java xuất hiện sau C đều mượn cú pháp "for" của họ từ C.


Vì vậy, triết lý của ( ,vs ;) là (yếu hơn so với phá vỡ mạnh hơn), chứ không phải (tuple- vs trình phân tách chuỗi), phải không? Đối với tôi, không rõ ràng liệu các đối số hoặc các câu lệnh có cần ngắt mạnh hơn hay không (như trong nhiều trường hợp cho một chuỗi các câu lệnh, các dấu ngắt là ẩn (xem ví dụ JavaScript (ví dụ i++[line break]j++))), nhưng ít nhất bây giờ tôi hiểu được tại sao quy ước hiện tại không phải là "rõ ràng đảo ngược".
Piotr Migdal

@PiotrMigdal dấu phẩy như một dấu phân cách sẽ ngăn việc sử dụng toán hạng dấu phẩy và có thể ngụ ý các thành phần của vòng lặp for là các câu lệnh chứ không phải là biểu thức. Điều này có ý nghĩa quan trọng.

Nhận xét cuối cùng khiến tôi tò mò BCPL đã làm gì, nhưng rõ ràng đó là FOR i = e1 TO e2 BY e3 DO c(biểu thức e1..e3, lệnh c), gần giống với cú pháp của BASIC hơn. Nguồn
một CVn

1
@PiotrMigdal - "Triết lý" là bất cứ điều gì K & R và phần còn lại đã nghĩ lại vào năm 1970. Tôi không nghĩ rằng nó đã đi vào chiều sâu của suy nghĩ mà bạn tưởng tượng. (Họ đã cố gắng thực hiện một ngôn ngữ "cấp độ cao hơn" để tránh phải viết hàng loạt phần mềm chuyển đổi điện thoại trong trình biên dịch chương trình.)
Stephen C

Tôi chỉ cần kiểm tra; các forcú pháp đã được giới thiệu trong C (nó không phải là trong B hoặc BCPL).
Donal Fellows

60

Chúng tôi viết các vòng lặp như:

 for(x = 0; x < 10; x++)

Ngôn ngữ có thể đã được xác định để các vòng lặp trông giống như:

 for(x = 0, x < 10, x++)

Tuy nhiên, hãy nghĩ về cùng một vòng lặp được thực hiện bằng vòng lặp while:

 x = 0;
 while(x < 10)
 {
     x++;
 }

Lưu ý rằng x=0x++là các câu lệnh, kết thúc bằng dấu chấm phẩy. Chúng không phải là biểu thức như bạn sẽ có trong một cuộc gọi chức năng. Dấu chấm phẩy được sử dụng để phân tách các câu lệnh và vì hai trong số ba phần tử trong một vòng lặp for là các câu lệnh, đó là những gì được sử dụng ở đó. Một vòng lặp for chỉ là một phím tắt cho một vòng lặp while như vậy.

Ngoài ra, các đối số không thực sự hoạt động như đối số cho một chức năng. Thứ hai và thứ ba được đánh giá nhiều lần. Đúng là họ không phải là một chuỗi, nhưng họ cũng không phải là đối số chức năng.

Ngoài ra, thực tế là bạn có thể sử dụng dấu phẩy để có nhiều câu lệnh trong vòng lặp for thực sự là điều bạn có thể làm ngoài vòng lặp for.

x = 0, y= 3;

là một tuyên bố hoàn toàn hợp lệ ngay cả bên ngoài một vòng lặp for. Tôi không biết sử dụng thực tế ngoài vòng lặp for. Nhưng vấn đề là dấu phẩy luôn chia nhỏ các câu lệnh; nó không phải là một tính năng đặc biệt của vòng lặp for.


Chắc chắn, tôi hiểu rằng vòng lặp "while" là "cơ bản hơn". Nhưng "ký hiệu tay ngắn" như vậy không có nhiều ý nghĩa (ít nhất là với tôi), vì bạn có thể bắt đầu bằng x = 0; y = 0;và (bên trong dấu ngoặc nhọn) x++; y++;...
Piotr Migdal

2
@PiotrMigdal, oh bạn có thể. Quan điểm của tôi là các phần bên trong vòng lặp for là các câu lệnh (được phân tách bằng dấu chấm phẩy) không phải là biểu thức (được phân tách bằng dấu phẩy)
Winston Ewert

1
Tôi nhận được sự khác biệt, đối với tôi ;là điều tự nhiên đối với một chuỗi các câu lệnh , không nhất thiết phải tách bất kỳ câu lệnh nào (vì vậy nó có khác nhau không?). Và trong quy ước hiện tại, đôi khi kết thúc bằng việc tách các chuỗi câu lệnh bằng dấu phẩy bằng mọi cách ...
Piotr Migdal

3
@PiotrMigdal, các thành viên của các tổ chức / đoàn thể được phân tách bằng dấu chấm phẩy, nhưng những người đó không thực sự tuần tự. Vì vậy, nó chắc chắn không bị hạn chế trong một chuỗi các tuyên bố trong việc sử dụng nó. Vào cuối ngày, cú pháp không đi xuống hương vị.
Winston Ewert

Tôi không biết về bất kỳ việc sử dụng thực tế nào ngoài vòng lặp for --- Thế còn (foo)?bar++, qux++:bletch--- Nơi bạn muốn ?:biểu thức thực hiện hai việc thay vì chỉ một. Giá trị trả về nếu foođúng là qux, nhưng cả hai barquxđược tăng lên.

15

Trong C và C ++, đây là toán tử dấu phẩy, không chỉ là dấu phẩy.

Ngữ pháp cho một forvòng lặp là một cái gì đó như

for ([pre-expression]; [terminate-condition]; [increment-expression]) body-expression

Trong trường hợp câu hỏi của bạn:

pre-expression -> init1, init2
terminate-condition -> condition
increment-expression -> inc1, inc2

Lưu ý rằng toán tử dấu phẩy cho phép bạn thực hiện nhiều hành động trong một câu lệnh (như trình biên dịch nhìn thấy nó). Nếu đề xuất của bạn được thực hiện, sẽ có một sự mơ hồ trong ngữ pháp về việc khi lập trình viên có ý định viết một câu lệnh toán tử dấu phẩy hoặc dấu phân cách.

Nói tóm lại, ;biểu thị sự kết thúc của một tuyên bố. Một forvòng lặp là một từ khóa theo sau là một danh sách các báo cáo tùy chọn bao quanh bởi (). Câu lệnh toán tử dấu phẩy cho phép sử dụng ,trong một câu lệnh.


3
Vòng lặp for là một tập hợp các biểu thức được phân tách bằng dấu chấm phẩy. Các câu lệnh có thể nhiều hơn các biểu thức - người ta không thể trượt một câu lệnh tình huống hoặc nếu câu lệnh thành một phần vòng lặp. Đó là một hàm ý quan trọng để nói rằng các thành phần của vòng lặp for là các câu lệnh khi người ta nhìn vào dạng

@MichaelT: Nhưng trong C ++, cú pháp của một forvòng lặp rõ ràng cho phép một câu lệnh (khai báo) là phần đầu tiên của nó. (C ++ cho phép khai báo giữa chức năng, trái ngược với C89 tiền nhiệm của nó). Bạn không thể khái quát các câu như vậy trên các ngôn ngữ, ngay cả đối với 2 ngôn ngữ gần như C và C ++.
MSalters

@MichaelT Bạn có bỏ lỡ phần 'giống như' không?
James

@ James bạn có thể tránh được "cái gì đó như" bằng BNF thực tế for ( {<expression>}? ; {<expression>}? ; {<expression>}? ) <statement>cho Cfor ( for-init-statement; conditionopt ; expressionopt ) statementcho C ++ --- Các ';' không chỉ biểu thị một kết thúc câu lệnh. Một vòng lặp for không được theo sau bởi các câu lệnh kèm theo ().

8

Không có sự đảo ngược về khái niệm.

Dấu chấm phẩy trong C đại diện cho nhiều bộ phận chính hơn dấu phẩy. Họ tách riêng các tuyên bố và tuyên bố.

Các bộ phận chính trong vòng lặp for là có ba biểu thức (hoặc một khai báo và hai biểu thức) và một thân.

Dấu phẩy bạn thấy trong C cho các vòng lặp không phải là một phần của cú pháp của vòng lặp for. Chúng chỉ là biểu hiện của toán tử dấu phẩy.

Dấu phẩy là dấu phân cách chính giữa các đối số trong các lệnh gọi hàm và giữa các tham số trong khai báo hàm, nhưng dấu chấm phẩy không được sử dụng. Vòng lặp for là cú pháp đặc biệt; nó không có gì để làm với các chức năng hoặc các cuộc gọi chức năng.


2

Có thể đây là một cái gì đó cụ thể cho C / C ++, nhưng tôi đăng câu trả lời này, bởi vì cú pháp của sự chậm trễ mà bạn mô tả chủ yếu bị ảnh hưởng bởi C-Syntax.

Bên cạnh các câu hỏi đã trả lời trước đó là đúng, theo quan điểm kỹ thuật, đó cũng là vì trong C (và C ++), dấu phẩy thực sự là một toán tử , mà bạn thậm chí có thể quá tải . Sử dụng một toán tử dấu chấm phẩy ( operator;()) có thể làm cho việc viết các trình biên dịch trở nên khó khăn hơn, vì dấu chấm phẩy là dấu kết thúc biểu thức tiên đề.

Điều gì làm cho intersting này là một thực tế, rằng dấu phẩy được sử dụng rộng rãi như seperator trên tất cả các ngôn ngữ. Có vẻ như toán tử dấu phẩy là một ngoại lệ, chủ yếu được sử dụng để có được các forgiao diện với nhiều điều kiện hoạt động, vậy thỏa thuận là gì?

Trong thực tế, cái operator,được xây dựng để làm điều tương tự như trong các định nghĩa, danh sách đối số, v.v.: Nó đã được xây dựng để phân tách các biểu thức - điều mà cấu trúc cú pháp ,không thể làm được. Nó chỉ có thể tách rời những gì đã được xác định trong tiêu chuẩn.

Tuy nhiên dấu chấm phẩy không tách rời - nó chấm dứt . Và đây cũng là điều dẫn chúng ta trở lại câu hỏi ban đầu:

for (int a = 0, float b = 0.0f; a < 100 && b < 100.0f; a++, b += 1.0f)
    printf("%d: %f", a, b);

Dấu phẩy ngăn cách các biểu thức trong ba phần của vòng lặp, trong khi dấu chấm phẩy chấm dứt một phần (khởi tạo, điều kiện hoặc suy nghĩ lại) của định nghĩa vòng lặp.

Các ngôn ngữ lập trình mới hơn (như C #) có thể không cho phép quá tải toán tử dấu phẩy, nhưng rất có thể chúng giữ nguyên cú pháp, bởi vì thay đổi nó cảm thấy không tự nhiên.


Có một vấn đề với lập luận này. Trong một fortuyên bố, ;biểu tượng được sử dụng rõ ràng như một dấu phân cách. Nó tách 3 phần cú pháp của câu lệnh. Không có dấu chấm phẩy thứ 3 để "chấm dứt" danh sách biểu thức lũy tiến. Nó được chấm dứt bởi một mã thông báo khác - ).
Stephen C

0

Đối với tôi chúng được sử dụng ít ý nghĩa tương tự như ý nghĩa ngôn ngữ của chúng. Dấu phẩy được sử dụng với danh sách và dấu chấm phẩy với các phần riêng biệt hơn.

Trong func(a, b, c)chúng tôi có một danh sách các đối số.

instruction1; instruction2; instruction3 có thể là một danh sách nhưng một danh sách các hướng dẫn riêng biệt và độc lập.

Trong khi ở for ( init1, init2; condition; inc1, inc2 )chúng tôi có ba phần riêng biệt - một danh sách các khởi tạo, một điều kiện và một danh sách các biểu thức tăng dần.


0

Cách dễ nhất để xem nó là như sau:

for(x = 0; x < 10; x++)

Là:

for(
x = 0;
x < 10;
x++
)

Nói cách khác, những x = 0 thingy thực sự là một câu lệnh / hướng dẫn chứ không phải là một tham số. Bạn chèn một tuyên bố đó. Do đó chúng được phân tách bằng dấu chấm phẩy.

Trong thực tế không có cách nào chúng được phân tách bằng dấu phẩy. Khi nào thì lần cuối bạn chèn những thứ như x <10 làm tham số? Bạn làm điều đó nếu bạn muốn máy tính x <10 một lần và chèn kết quả của hoạt động đó làm tham số. Vì vậy, trong thế giới dấu phẩy, bạn sẽ đặt x <10 nếu bạn muốn truyền giá trị của x <0 cho một hàm.

Ở đây bạn xác định rằng chương trình sẽ kiểm tra x <10 mỗi khi vòng lặp được thông qua. Vì vậy, đó là một hướng dẫn.

x ++ chắc chắn là một hướng dẫn khác.

Đó là tất cả các hướng dẫn. Vì vậy, chúng được phân tách bằng dấu chấm phẩy.


Đó không phải là một tuyên bố. Đó là một biểu thức được phân tách bằng dấu chấm phẩy. Một tuyên bố là hoàn toàn khác nhau.

x <10 có thể là một biểu thức (thường được phân tách bằng dấu chấm phẩy. x = 0 chắc chắn là một tuyên bố / hướng dẫn.
user4951

Nhìn vào bnf cho C - nếu vòng lặp for là câu lệnh, người ta có thể sử dụng các câu lệnh khác như câu lệnh khác for switchhoặc return bên trong định nghĩa vòng lặp for (tức là for(int i = 0; if(i > 1024) { return; } ; switch (i % 3) { case 0; case 1: i++; case 2: i++; } ) { ... }) --- bạn không thể. Đó không phải là một tuyên bố. Thay vào đó, nó được định nghĩa làfor ( {<expression>}? ; {<expression>}? ; {<expression>}? ) <statement>

Lạ thật. int i = 0 là một biểu thức hoàn toàn đúng, nhưng chúng tôi chủ yếu thực hiện nó để khai báo một int cụ thể là i và gán 0 cho nó (nó cũng trả về 0 nhưng là một hiệu ứng phụ. Bạn không thể làm cho ({int i = 0; j = i}; j <0; cout << "Xin chào thế giới") bạn có thể không? Hoặc tôi nghĩ bạn có thể.
user4951

1
@JimThio: Có thể bạn không biết về nó, nhưng "tuyên bố" và "biểu hiện" có ý nghĩa rất chính xác trong các tiêu chuẩn ngôn ngữ. int i = 0chắc chắn KHÔNG phải là một biểu thức. Tập hợp các quy tắc mô tả một biểu thức khá phức tạp, xem xét những gì có thể tạo thành một biểu thức, nhưng cấu trúc TYPE NAME = EXPRESSIONkhông khớp với các quy tắc đó.
MSalters
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.