Những cấu trúc điều khiển thay thế hữu ích nào bạn biết? [đóng cửa]


12

Câu hỏi tương tự đã được đóng lại trên SO.

Đôi khi, khi chúng tôi lập trình, chúng tôi thấy rằng một số cấu trúc điều khiển cụ thể sẽ rất hữu ích cho chúng tôi, nhưng không có sẵn trực tiếp trong ngôn ngữ lập trình của chúng tôi.

Những cấu trúc điều khiển thay thế nào bạn nghĩ là một cách hữu ích để tổ chức tính toán?

Mục tiêu ở đây là để có được những cách nghĩ mới về cấu trúc mã, để cải thiện sự phân chia và lý luận.

Bạn có thể tạo một cú pháp mong muốn / ngữ nghĩa không có sẵn ngay bây giờ hoặc trích dẫn một cấu trúc điều khiển ít được biết đến trên một ngôn ngữ lập trình tồn tại.

Câu trả lời nên đưa ra ý tưởng cho một ngôn ngữ lập trình mới hoặc tăng cường một ngôn ngữ thực tế.

Hãy nghĩ về điều này như là động não, vì vậy hãy đăng một cái gì đó bạn nghĩ là một ý tưởng điên rồ nhưng nó có thể khả thi trong một số tình huống.

Đó là về lập trình bắt buộc.


1
Tại sao câu hỏi này phải là về "lập trình mệnh lệnh"?
missingfaktor

@missingfaktor: Bởi vì cấu trúc điều khiển là về lập trình mệnh lệnh. Chắc chắn, một số vấn đề có thể được giải quyết bằng cách tiếp cận chức năng hoặc theo cách khác nhưng các mô hình khác không đặt cấu trúc điều khiển vào trung tâm của sự phát triển. Nhưng dù sao bạn cũng có thể sáng tạo.
Maniero

3
Chà, tôi sẽ chia sẻ Ghép mẫu với Vệ binh, vì những thứ đó chung chung hơn là các tuyên bố về Case và If-Thens, nhưng nếu đây không phải là khu vực FP, tôi đoán đó chỉ là Ghép mẫu cho phần còn lại của chúng tôi!
CodexArcanum

@CodexArcanum: Chà, Ghép mẫu là một cấu trúc rất giống với các cấu trúc mệnh lệnh. Thật vậy, Bản thân khớp mẫu là một thay thế cho các cấu trúc điều khiển bắt buộc. Đừng ngại ;-)
Maniero

Nhìn vào tất cả các câu trả lời làm tôi vui rằng không có đề xuất nào tồn tại trong cuộc sống thực (và hy vọng sẽ không bao giờ có). Xin lỗi :)
serg

Câu trả lời:


14

OK, đây là một câu hỏi thú vị.

Tôi cũng muốn có một vị tướng elsetrong một thời gian và cho các vòng lặp, khi điều kiện không đúng trong bài kiểm tra đầu tiên :

while (condition) {
    // process
}
else {
    // condition was never true
}

Điều này tránh sự tính toán lại vụng về của điều kiện hoặc lưu trữ nó trong một biến.


Python có cái này.
Barry Brown

Chào! Cuối cùng, một sự lựa chọn kỳ quặc của Python mà tôi đồng ý! ;-)
Macneil

5
Không. Mệnh đề khác trong vòng lặp Python for / while được thực thi nếu vòng lặp kết thúc bình thường, điều đó có nghĩa là vòng lặp không bị chấm dứt thông qua câu lệnh break hoặc return hoặc ngoại lệ. Trong các vòng lặp, clasuse khác được thực thi sau khi chuỗi các phần tử được lặp đi lặp lại đã hết, và trong khi các vòng lặp, nó được thực thi sau khi điều kiện vòng lặp lần đầu tiên đánh giá thành Sai.
thuốc

4
một yêu cầu để thêm while ... elsecấu trúc theo cách Macneil mô tả nó với PHP. Tôi nghĩ đó là một ý tưởng tuyệt vời, bởi vì "đây là kết quả / không có kết quả" là một thành ngữ khá phổ biến trong các ứng dụng web.
Dean Harding

1
@SnOrfus: Anh ta muốn một khối mã (tách biệt với khối của vòng lặp) thực thi CHỈ nếu điều kiện while không bao giờ được đáp ứng. Do-While thực thi khối mã của vòng lặp (không phải một số khối riêng biệt) một lần bất kể trong khi có điều kiện.
Quân đoàn

10

Tại sao không nghiền một vài câu trả lời thành một?

while (expr) {

    // Executed every iteration, unless first{} is present.
    // May be explicitly called rest{} if you like first{} to come first.

    // Blocks may return results, and consequently be used in expressions.
    return expr;

} first {

    // Executed only on the first iteration.

} pre {

    // Executed before every iteration.

} post {

    // Executed after every iteration.

} catch (oops) {

    // All blocks are implicitly try{}ed if followed by a catch{}.

} finally {

    // Executes after the block completes, regardless of exceptions.

} else {

    // Executed if the loop body or rest{} never executes.

} never {

    // Executes only when a client is present.

} drop (bad, worse), // Explicitly ignore certain exceptions.
  until (expr);      // Here, have a post-body condition, too.

Một cú pháp xây dựng kiểm soát dòng chảy mở rộng, tổng quát trong một ngôn ngữ bắt buộc sẽ khá hữu ích và giải trí. Cho đến khi điều đó xuất hiện, đoán tôi sẽ chỉ sử dụng Lisp hoặc một cái gì đó.


5
Không hữu ích, khó hiểu. Không giải trí, chậm.
Josh K

2
@Josh K: Vâng, bạn nói đúng. Cái gì, bạn nghĩ rằng tôi không đồng ý? Câu trả lời này là cắn lưỡi để không cười.
Jon Purdy

Nếu nó có ý mỉa mai thì chắc chắn nó đã thành công!
Josh K

11
+1 Làm tôi cười. Cần một mệnh đề "giữa", để mã thực thi giữa các lần lặp.
Barry Brown

1
+1, nhưng nó cũng cần seventh { ... }.
j_random_hacker

7

Cấu trúc điều khiển như các chức năng.

Tôi muốn for, if, else, while, vv để có chức năng, không phải cấu trúc đặc biệt.

Tôi muốn return, try/exceptgotolà dẫn xuất của sự tiếp nối.

Tất nhiên điều này ít liên quan đến một cấu trúc điều khiển cụ thể và nhiều thứ khác liên quan đến cách bạn nhìn thấy các cấu trúc điều khiển nói chung, meta của các cấu trúc điều khiển.


2
Continuations là một khái niệm nặng nề cho một ngôn ngữ, và có những lý do chính đáng để loại bỏ chúng khỏi nhiều ngôn ngữ.
David Thornley

Tôi không đồng ý; có for, if, else, và whilenhư các chức năng làm cho tôi suy ra rằng các tham số cho những chức năng cần phải được thực hiện một cách lười biếng để cư xử giống như các cấu trúc ban đầu. Có khả năng thực hiện lười biếng sẽ tốt đẹp.
Học viên của Tiến sĩ Wily

1
@David: Chỉ vì chúng ở đó không có nghĩa là bạn phải sử dụng chúng. Các lập trình viên thông thường có thể sử dụng những gì họ đã quen return, ngoại lệ, v.v. Nhưng bây giờ, lập trình viên chuyên gia có một công cụ bổ sung mạnh mẽ trong hộp công cụ của anh ta nếu có nhu cầu. Bên cạnh đó, các ngôn ngữ IMHO không nên "chết lặng". Ngôn ngữ nên có khả năng hỗ trợ tăng chuyên môn và tăng trưởng kiến ​​thức CS.
Dietbuddha

1
@dietbuddha: (tiếp) Tôi không nói rằng các phần tiếp theo là xấu, tôi đang nói rằng chúng nặng, và làm cho chúng có thể sử dụng được có ý nghĩa đối với ngôn ngữ, một cái gì đó giống như ngoại lệ. Hơn nữa, việc tiếp tục có khả năng buộc thay đổi cách mọi người sử dụng ngôn ngữ, một lần nữa giống như các ngoại lệ trong C ++. Một ngôn ngữ hỗ trợ các phần tiếp theo sẽ khác với ngôn ngữ không, theo cả hai cách tốt và xấu.
David Thornley

1
@ Steve314 - longjmp không phải là sự tiếp nối mặc dù có những điểm tương đồng. Tiếp tục lưu toàn bộ trạng thái (tất cả các địa phương, toàn cầu và ngăn xếp). longjmp lưu một con trỏ ngăn xếp để nếu bạn thoát khỏi phạm vi nơi setjmp được sử dụng, bạn sẽ nhận được một segfault do khung không còn tồn tại. Tôi tin rằng cũng có một số hạn chế trong các biến nó lưu.
dietbuddha

6

Bài viết được liên kết chắc chắn hiểu đúng về Vòng lặp N + 1/2 của Donald Knuth . Thể hiện bằng C / C ++ / Java:

for (;;) {
  get next element;
  if (at the end) break;
  process the element;
}

Điều này hữu ích để đọc các dòng hoặc ký tự từ một tệp, kiểm tra xem bạn đã đạt EOF chưa, và sau đó xử lý nó. Tôi đã quá quen với việc nhìn thấy mô hình for(;;)..if(..)break;xuất hiện rằng nó là thành ngữ đối với tôi. (Trước khi tôi đọc bài báo Knuth, được in lại trong cuốn sách Lập trình văn học , bài này đã từng là "wtf?".)

Knuth đề xuất các từ khóa loop/while/repeat:

loop:
  S;
while C:
  T;
repeat

Trường hợp STlà chủ sở hữu vị trí cho một loạt các câu lệnh bằng 0 hoặc nhiều hơn và Clà một điều kiện boolean. Nếu không có Scâu lệnh thì đó sẽ là một vòng lặp while và nếu không có Tcâu lệnh thì đó sẽ là một vòng lặp do.

Bản thân cấu trúc này có thể được khái quát bằng cách cho phép không hoặc nhiều while Cmệnh đề, làm cho nó hoàn hảo để thể hiện các vòng lặp vô hạn và sau đó một số điều kiện hiếm hơn sẽ cần hai kiểm tra.

Trong cùng một bài viết, Knuth đã đề xuất một cơ chế báo hiệu sẽ là một phiên bản cục bộ của các trường hợp ném / bắt ngoại lệ (như là một cách thay thế cho việc sử dụng goto).

Cho tôi? Tôi muốn Java hỗ trợ tối ưu hóa cuộc gọi đuôi, để tôi có thể diễn đạt bất kỳ cấu trúc điều khiển chung nào khi cần.


Cập nhật: Tôi quên đề cập rằng nhiều lập trình viên C / C ++ / Java đã khắc phục điều này bằng cách sử dụng một bài tập được nhúng trong điều kiện while:

while ((c = getc(f)) != -1) {
   T;
}

Sử dụng các thuật ngữ từ cấu trúc của Knuth, điều này được phép khi SCcó thể được kết hợp thành một biểu thức. Một số người ghét nhìn thấy bài tập được nhúng ở trên, trong khi những người khác ghét nhìn thấy phần breakfor (;;)trên. Nhưng khi SCkhông thể được kết hợp, chẳng hạn như khi Scó nhiều câu lệnh, thì câu lệnh for (;;)thay thế duy nhất mà không lặp lại mã. Cách khác là đơn giản là sao chép Smã:

S;
while (C) {
  T;
  S;
}

Sự loop/while/repeatthay thế của Knuth có vẻ tốt hơn nhiều.


Vòng lặp lặp lại đó trông rất giống repeat-untilvòng lặp Pascal .
Mason Wheeler

@Mason: Sự khác biệt là có hai nơi bạn có thể chèn các câu lệnh cộng với điều kiện của bạn, không chỉ một.
Macneil

Ồ, tôi thấy những gì đang xảy ra. Vâng, thật thú vị ...
Mason Wheeler

ANSI Basic có một do while ... loop until- cả điều kiện tiên quyết và hậu điều kiện là tùy chọn và tôi (mơ hồ) nhớ sử dụng cả hai trong một vòng lặp. Đối với điều kiện trung bình của bạn, có Ada exit when ...;. Ưu điểm chính if ... break;là bạn có thể viết exit loopname when ...;để thoát ra khỏi nhiều vòng lặp (nhưng không nhất thiết là tất cả) cùng một lúc. Có lẽ hơi rõ hơn so với phá vỡ, quá.
Steve314

Điều buồn cười. Ada có chính xác tính năng đó.
John R. Strohm

6

Ngôn ngữ BCPL có một valueofbiểu thức có thể được sử dụng để biến một chuỗi các câu lệnh thành một biểu thức:

foo(a, b, valueof {some series of statements; resultis v});

Nơi some series of statementscó thể là bất cứ điều gì và toàn bộ valueofđánh giá v.

Điều này có thể có ích trong Java khi bạn cần tính toán một đối số để gọi một this()hoặc super()(yêu cầu không có gì xảy ra trước nó). Tất nhiên, bạn chỉ có thể viết một phương thức riêng biệt, nhưng đó có thể là một nỗi đau nếu bạn cần truyền vào nhiều giá trị cục bộ cho ngữ cảnh.

Nếu bạn có thể sử dụng finalcho các biến cần thiết, bạn đã có thể thực hiện valueoftrong Java bằng các lớp bên trong ẩn danh:

foo(a, b, new Object(){String valueof(){
    String v ...; some series of statements; return v;}}.valueof());

1
GCC có một phần mở rộng cho điều này - ({ statement1; statement2; ...; result-expr; }). Tôi cũng đã thấy tương tự ở nơi khác, nhưng tôi không nhớ là ở đâu. Có lẽ tất cả được sao chép từ BCPL.
Steve314

6
unless(condition) {
  // ...
}

thực hiện tương tự như:

if(!condition) {
  // ...
}

repeat {
  // ...
} until(condition)

thực hiện tương tự như:

do {
  // ...
} while(!condition)

Bạn có thể muốn đến Lisp ...
duros

1
Hoặc Ruby, nó có cú pháp tương tự cho unless.
Josh K

2
Trông khá Perlish. Có nhiều hơn một cách để làm điều đó.

2
@ Steve314 Bạn phải sử dụng một kiểu niềng răng không chính xác . ;-)
Orble

1
@ Tổ chức - hoàn toàn không. Mọi người khác sử dụng một kiểu niềng răng không chính xác.
Steve314

5

Một lưu ý khác, tôi muốn thấy sự hỗ trợ tốt hơn cho các trình vòng lặp trong các ngôn ngữ lập trình. Đặc biệt, khi bạn muốn đi xuống hai bộ sưu tập theo cặp :

for (String s, Integer i : stringsSet, integersSet) {
    // use the pair (s, i)
}

Một số ngôn ngữ động có thể đã có điều này hoặc dễ dàng hỗ trợ thông qua các thư viện và macro, nhưng tôi nghĩ rằng đây là tinh thần của câu hỏi của bạn.

Nếu hai bộ không cùng kích thước, thì nó có thể đưa ra một ngoại lệ hoặc bạn có thể có một elsevòng lặp sau để báo hiệu có sự khác biệt về kích thước.

Đương nhiên, bạn có thể khái quát hóa điều này để đi xuống ba danh sách trở lên.


Cập nhật: Cũng hữu ích khi thực hiện sản phẩm Cartesian giữa các lần lặp:

for (String s, Integer i : stringsSet * integersSet) {
    // use the pair (s, i), each s with each i
}

đó sẽ không là gì ngoài các vòng lặp lồng nhau:

for (String s : stringsSet) {
    for (Integer i : integersSet) {
        // use the pair (s, i), each s with each i
    }
}

Tôi có một chút lo ngại rằng giữa hai ký hiệu tôi đã cung cấp ở đây có sự khác biệt về số lượng cặp O (n) và O (n ^ 2), chỉ có sự thay đổi của một ký tự.


2
Trong Python có zip và zip_longest làm điều này.
thuốc

Thật tuyệt, có lẽ tôi đang đánh giá thấp Python và sẽ cho nó một cái nhìn thứ hai sau nhiều năm. Điều đó nhắc nhở tôi, đôi khi bạn cũng muốn sản phẩm của Cartesian, tương đương với các vòng lặp lồng nhau.
Macneil

1
Cả hai trường hợp trong Scala: paste.pocoo.org/show/297429
missingfaktor

1
Liên quan đến sản phẩm của Cartesian: Một lần nữa, Python có nó. for a, b, c in itertools.product(iter1, iter2, iter3):cung cấp cho bạn các sản phẩm Cartesian đánh giá lười biếng. Cái gì vậy Bạn muốn hoán vị và kết hợp của một trình vòng lặp nhất định quá? itertools.permutations, itertools.combinations.
aaronasterling

1
Phối cảnh Haskell: sử dụng "zipWith" cho trường hợp đầu tiên và hiểu danh sách hoặc danh sách đơn vị cho lần thứ hai. Giống như các biểu đồ Python / Scala, nhưng thanh lịch hơn. :)
LennyProgrammer

5

Có cái gọi là "Vòng lặp của Dijkstra" (còn được gọi là "Vòng bảo vệ của Dijkstra"). Nó được định nghĩa trong Ngôn ngữ lệnh được bảo vệ (GCL) . Bạn có thể tìm thấy một số thông tin về cú pháp và ngữ nghĩa của nó trong bài viết Wikipedia ở trên tại phần 6 Lặp lại: do .

Ngày nay tôi thực sự biết một ngôn ngữ lập trình hỗ trợ chiến lược điều khiển này trực tiếp. Đó là Oberon-07 (PDF, 70 KB). Và nó hỗ trợ "Dijkstra's Loop" dưới dạng câu lệnh while. Hãy xem phần 9.6. Trong khi các tuyên bố trong PDF trên.

WHILE m > n DO m := m – n 
ELSIF n > m DO n := n – m 
END

PS Đây là bản sao câu trả lời SO của tôi .


Có vẻ như trình kiểm tra mô hình Spin cũng có cấu trúc này, với ngữ nghĩa giống hệt nhau và cú pháp cơ bản giống hệt nhau.
j_random_hacker

4

Biểu thức kiểu biểu tượng với tính năng quay lui tích hợp.

Python nhận được rất nhiều lợi ích của Trình tạo biểu tượng - và nói chung là thực hiện tốt hơn chúng, IMO. Và về nguyên tắc, việc quay lại chỉ là một kiểu ném ngoại lệ, nhưng đó là sự đơn giản của các biểu thức tương đương với ...

x = (a / b) else c;

để xử lý các trường hợp thất bại như chia cho số không.

Trường hợp Icon bị hỏng - không có toán tử so sánh trả về boolean. Các so sánh luôn luôn thành công hoặc kích hoạt quay lui, và có một số vấn đề ngữ nghĩa khác mà bây giờ tôi đang cố gắng để nhớ rằng ... tốt, hãy nói rằng nó có lẽ bị đè nén nhiều hơn là bị lãng quên.

Tôi luôn nghĩ rằng họ nên có một ifbiểu hiện không có phần nào khác - if (condition, success-value)loại điều, quay lại nếu điều kiện trả về sai - và bỏ các so sánh kỳ lạ.

EDIT tôi nhớ - rõ ràng thực sự. So sánh với hai đối số thành công hoặc thất bại - nó không tính toán một giá trị mới để trả về. Vì vậy, khi nó thành công, những gì không nó trở lại? Trả lời - một trong những lý lẽ. Nhưng nếu bạn viết a > b, đó là đối số hợp lý để trả về - ahoặc b? Và nếu bạn viết b < athay thế thì sao? Tôi nghĩ rằng nó luôn trả về đúng đối số, điều này có ý nghĩa như mọi thứ, nhưng nó vẫn thường có vẻ như là đối số sai đối với tôi.


4

Đây chỉ là một ý tưởng chung và cú pháp:

if (cond)
   //do something
else (cond)
   //do something
also (cond)
   //do something
else
   //do something
end

Điều kiện CSONG luôn được đánh giá. ELSE hoạt động như bình thường.

Nó hoạt động cho trường hợp quá. Có lẽ đó là một cách tốt để loại bỏ tuyên bố phá vỡ:

case (exp)
   also (const)
      //do something
   else (const)
      //do something
   also (const)
      //do something
   else
      //do something
end

có thể được đọc là:

switch (exp)
   case (const)
      //do something
   case (const)
      //do something
      break
   case (const)
      //do something
   default
      //do something
end

Tôi không biết điều này hữu ích hay đơn giản để đọc nhưng đó là một ví dụ.


3

Phong cách tiếp tục đến với tâm trí. Sau đó, tất nhiên, bạn cũng muốn có Tối ưu hóa cuộc gọi đuôi .


1
Không phải là một fan hâm mộ lớn của điều này, và tôi không tin đó thực sự là một "cấu trúc điều khiển" mà là một khối xây dựng trong ngôn ngữ chức năng và phong cách lập trình. Node.js dường như khá gắn kết với nó mặc dù.
Josh K

1
Tất nhiên đó là một cấu trúc điều khiển. Nó không chỉ là một từ đi kèm với một từ khóa như 'nếu' hoặc 'cho', mà là một mô hình cho luồng điều khiển cấu trúc (do đó, cấu trúc điều khiển). Nó thậm chí còn được sử dụng đằng sau hậu trường bởi nhiều trình biên dịch. Và nó cũng không giới hạn ở FL. Bạn cần các chức năng như các đối tượng hạng nhất mặc dù.
thuốc

1
Bạn nhận được tối ưu hóa cuộc gọi đuôi trong C và C ++ những ngày này, nhưng IMO nó bỏ lỡ điểm. Vấn đề là nó một tối ưu hóa. Trong Scheme, một cuộc gọi đuôi chính hãng là rõ ràng. Trong C ++ đặc biệt, nhiều điều có thể có nghĩa là cuộc gọi đuôi của bạn không phải là cuộc gọi đuôi. Và ngăn xếp tràn có nghĩa là ứng dụng của bạn bị hỏng. IMO, cần có một cái gì đó giống như một goto return ...;tuyên bố, làm cho ý định gọi đuôi rõ ràng, vì vậy nếu trình biên dịch không thể làm cho nó lặp đi lặp lại thì đó là một lỗi.
Steve314

1
@Macneil - mà tôi biết chắc chắn, tối ưu hóa cuộc gọi đuôi được thực hiện trong GCC, Clang và Visual C ++. GCC có các chuyển đổi tinh vi hơn từ đệ quy sang lặp, có thể xử lý một số trường hợp không theo dõi đệ quy. Nhưng rất nhiều có thể đi sai. Truyền con trỏ đến một biến cục bộ trong lệnh gọi đuôi đó và khung ngăn xếp không thể bị loại bỏ, vì biến này cần được giữ nguyên. Trong C ++, các hàm hủy biến cục bộ thường sẽ xảy ra sau khi cuộc gọi "đuôi" trả về, nghĩa là nó hoàn toàn không phải là một cuộc gọi đuôi.
Steve314

1
@Mike - đó là quan điểm của tôi. Nếu bạn sử dụng kiểu mã hóa đệ quy, nếu không có đuôi gọi "tối ưu hóa" thì mã của bạn có khả năng bị hỏng, vì lỗi tràn ngăn xếp là một lỗi. Trong C ++, đó là một tối ưu hóa - để trình biên dịch thực hiện tối ưu hóa, do đó bạn không cần phải ổn, nhưng bạn không thể dựa vào chúng. Nếu bạn muốn tự do viết theo kiểu đệ quy, nhưng không muốn phải lo lắng về các vấn đề về độ sâu của ngăn xếp, loại bỏ cuộc gọi đuôi không phải là tối ưu hóa - đó là vấn đề chính xác. Nếu đệ quy là cách tốt nhất để mã hóa một cái gì đó, bạn sẽ có thể làm điều đó - không phải lo lắng về việc tràn ngăn xếp.
Steve314

3

Phân nhánh luồng liền mạch, nó có cú pháp giống như một hàm, nhưng thực thi trong một luồng riêng biệt và không thể truy cập dữ liệu ban đầu không được truyền cho nó.

branch foo(data, to, be, processed){
    //code
    return [resulting, data]
}

Khi một nhánh được gọi nó sẽ ngay lập tức trả về một tay cầm.

handle=foo(here, is, some, data)

Tay cầm có thể được sử dụng để kiểm tra nếu tác vụ được thực hiện.

handle.finished() //True if the execution is complete

Nếu kết quả được yêu cầu trước khi thực hiện xong, luồng chính sẽ chỉ chờ.

[result, storage]=handle.result()

Điều này sẽ không bao gồm các kịch bản đa luồng nâng cao hơn, mà là cung cấp một cách dễ dàng bắt đầu để sử dụng nhiều lõi.


Hãy xem Cilk, đây là một phần mở rộng rất đơn giản và sạch sẽ của C: en.wikipedia.org/wiki/Cilk . Tôi không biết nếu nó có một handle.finished()bài kiểm tra, nhưng spawnsynclà tất cả các bạn cần cho 90% công việc lập trình song song.
j_random_hacker

3
if (cond)
   //do something
else (cond)
   //do something
else (cond)
   //do something
first
   //do something
then
   //do something
else (cond)
   //do something
else
   //do something
end

Các khối FIRST và THEN chạy nếu bất kỳ 3 điều kiện nào được đánh giá là đúng. Khối FIRST chạy trước khối điều kiện và THEN chạy sau khi khối điều kiện đã chạy.

ELSE có điều kiện hoặc viết cuối cùng sau câu lệnh FIRST và THEN độc lập với các khối này.

Nó có thể đọc là:

if (cond)
   first()
   //do something
   then()
else (cond)
   first()
   //do something
   then()
else (cond)
   first()
   //do something
   then()
else (cond)
   //do something
else
   //do something
end


function first()
   //do something
return
function then()
   //do something
return

Các chức năng này chỉ là một hình thức để đọc. Họ sẽ không tạo ra phạm vi. Nó giống như một gosub / trở về từ Basic.

Hữu ích và dễ đọc như là vấn đề thảo luận.


2

Đôi khi tôi thấy mình viết một vòng lặp cần phải làm một cái gì đó khác biệt trong lần lặp đầu tiên. Ví dụ: hiển thị thẻ <th> thay vì thẻ <td>.

Tôi xử lý tình huống này với một cờ boolean. Một cái gì đó như thế này:

first = true

while (some_condition)
    if (first)
        do_something
        first = false
    else
        do_something_else

Có vẻ ngớ ngẩn khi kiểm tra giá trị của firstmỗi lần lặp khi nó sẽ sai hầu hết thời gian.

Tôi muốn có một tùy chọn lặp để chỉ định một thân vòng lặp khác nhau trong lần lặp đầu tiên. Sẽ không cần một biến riêng biệt. Mã được biên dịch sẽ không cần một, bởi vì mã được tạo sẽ có hai thân, một cho lần lặp đầu tiên và một cho phần còn lại.


Câu hỏi SO có một ý tưởng tương tự, nhưng với toán tử "thì" được sử dụng trên các giá trị. Theo cách đó, có lẽ bạn không có mã trùng lặp. Ví dụ:print(out, first "<th>" then "<td>")
MacNeil

1
Cách tốt hơn là bắt đầu lặp lại +1.
Josh K

1
Trường hợp phổ biến là lặp với xử lý giữa, ví dụ liệt kê các mục bằng dấu phân cách dấu phẩy. Nghiêm túc, đó là if (!first) gimme-a-comma ();, nhưng nhiều điều tương tự. Mặc dù vậy, một sự phản đối mà tôi có - nếu bạn kết thúc nó một cách thích hợp, bạn sẽ kết thúc với những thứ như phương thức nối chuỗi Python - tuy nhiên thường thì bạn cần mẫu cơ bản, vòng lặp bên dưới không cần phải viết lại thường xuyên.
Steve314

Như Josh nói, tất nhiên, kéo mục đầu tiên ra khỏi vòng lặp là hợp lệ. Trong trường hợp dấu phẩy phân tách có nghĩa là mã trùng lặp, nhưng đó có thể là một lệnh gọi hàm trùng lặp. Cá nhân, tôi thích sự không hiệu quả của if (!first), nhưng tối ưu hóa vi mô có thể làm tăng sự phản đối dự đoán chi nhánh.
Steve314

1
@Barry Brown: Việc hủy vòng lặp thứ 1 của vòng lặp có thể hoặc không thể nhanh hơn việc kiểm tra cờ boolean. Một người dự đoán nhánh tốt sẽ dự đoán sai ở lần lặp thứ 2 tồi tệ nhất, tôi dự đoán :) Tôi thích sử dụng if (!first)và để một trình biên dịch tối ưu hóa quyết định liệu thân vòng lặp có đủ nhỏ để không kiểm soát và loại bỏ firstlà một chiến thắng ròng hay không.
j_random_hacker

1

[được sao chép từ câu trả lời của riêng tôi trên stackoverflow]


ignoring - Để bỏ qua các ngoại lệ xảy ra trong một khối mã nhất định.

try {
  foo()
} catch {
  case ex: SomeException => /* ignore */
  case ex: SomeOtherException => /* ignore */
}

Với cấu trúc điều khiển bỏ qua, bạn có thể viết nó chính xác hơn và dễ đọc hơn như:

ignoring(classOf[SomeException], classOf[SomeOtherException]) {
  foo()
}

[Scala cung cấp điều này (và nhiều cấu trúc điều khiển xử lý ngoại lệ khác) trong thư viện chuẩn của nó, trong gói produc.control. ]


4
Các ngoại lệ không nên bỏ qua.
Josh K

1
Ngoại trừ điều đó, trong bối cảnh, một ngoại lệ có thể không phải là một lỗi.
Steve314

1
@Josh, @Steve: Có những trường hợp bạn muốn bỏ qua các ngoại lệ. Xem chủ đề này cho một số trường hợp như vậy.
missingfaktor

Một ngoại lệ là một cảnh báo rằng một cái gì đó có thể sai. Một try..catchkhối trống là một thứ; nó buộc bạn phải nhận ra lỗi và cố tình bỏ qua nó; trong khi việc ném các đoạn mã dưới sự bỏ qua toàn cầu có thể dẫn đến các vấn đề khi những ngoại lệ đó bị ném, cũng như dẫn đến thói quen lập trình kém.
Josh K

@Josh - Chỉ cần xóa hai bình luận vì tôi không nghĩ thẳng - nhưng hy vọng rất cao cho ý kiến ​​này. Nếu tuyên bố đó là toàn cầu, tôi có thể đồng ý - nhưng nó có vẻ giống như một cấu trúc khối đối với tôi. IOW nó giống như một trykhối, ngoại trừ nó liệt kê các ngoại lệ mà nó bắt được và bỏ qua phía trước, thay vì sau này. Đó thậm chí có thể là một lợi thế dễ đọc - ví dụ: cho người đọc biết rằng một tệp bị thiếu không phải là lỗi ngay cả trước khi họ đọc cuộc gọi mở.
Steve314

1

Thay vì:

switch(myEnum) {
  case MyEnum.Val1: do1(); ...
  case MyEnum.Val2: do2(); ...
....

Làm điều đó với Python, hoặc bây giờ là cách C #:

action = val2func[myEnum]
action()

Scala có rất nhiều tính năng mới.

Cuối cùng, các ngôn ngữ như Clojure có thể được mở rộng để cung cấp chức năng bổ sung.


1
C cũng có thể làm điều này. Và Pascal. Có lẽ tất cả những ngôn ngữ cũ của thập niên 70/80/90. Một mảng các hàm trở thành một mảng các con trỏ hàm, nhưng đó không phải là vấn đề lớn. Nơi dễ dàng hơn là khi bạn có các chức năng ẩn danh - bạn biết đấy, như trong Lisp, ML, ...
Steve314

Steve314 - hiệu chỉnh - đây là một ví dụ về từ điển ánh xạ bất kỳ giá trị nào tới hàm. Đây là nơi mọi thứ trở nên sạch hơn một chút so với hầu hết mọi thứ từ thập niên 70/80/90 có thể làm.
Công việc

1

Tôi có hai ý tưởng.

Thường thì tôi thấy rằng tôi đang lặp lại chính mình trong catchcác khối. Điều này có thể được giúp đỡ phần nào thông qua các phương pháp trích xuất, nhưng điều đó có thể gây ra sự lộn xộn không cần thiết nếu các phương thức này rất ngắn hoặc nếu không thì không xứng đáng với một phương pháp. Vì vậy, sẽ rất tốt nếu lồng catchcác khối:

try {
    // Save something
} catch (Exception e) {
    // Something we do for all Exceptions
    catch (ProcessingException e) {
        // Something we do for all Processing exceptions
        catch (DBExcpetion e) {
            // DBExceptions are a subclass of ProcessingException
        }
        catch (BusinessRuleException e) {
            // BusinessRuleExceptions are also a subclass of ProcessingException
        }
    }
    // Something we do after specific sub class Exceptions
 }

Trong lập trình web, tôi cũng thường thấy mình thường làm một việc như thế này (đây không phải là một ví dụ thực tế vì vậy đừng phân tích các trường hợp hư cấu):

Account a = getSavedAccount();
if (a == null) {
    a = getAccountFromSessionId();
}
if (a == null) {
    a = getAccountFromCookieId();
}
if (a == null) {
    a = createNewAccount();
}

Trong Javascript (tốt, ECMAScript và có thể những người khác mà tôi không quen thuộc), vì bất kỳ giá trị nào cũng có thể được đánh giá là điều kiện, ||có thể giúp ích.

var a = getAFromLocation1() || getAFromLocation2() || default;

Tôi thực sự thích vẻ ngoài đó và tôi muốn nhiều ngôn ngữ hơn, đặc biệt là một số ngôn ngữ ở phía máy chủ, đã hỗ trợ cho nó. (PHP có thể đánh giá bất cứ điều gì như một điều kiện, nhưng chuyển đổi toàn bộ biểu thức điều kiện thành boolean thay vì bảo toàn giá trị. Tôi không biết về Python hoặc Ruby.) Nó có thể trở nên khó sử dụng sau ba trường hợp hoặc hơn, nhưng nếu bạn có nhiều hơn hơn ba trường hợp, bạn cũng có thể có một thiết kế phần mềm xấu.


Python làm một cái gì đó giống như | | đánh giá, nhưng khi cuối cùng nó có x if c else ycú pháp biểu thức điều kiện , một lý do chính đã xảy ra là vì rất nhiều biểu thức sử dụng các ngữ nghĩa này cho ||&&đã bị lỗi một cách tinh vi. IIRC một trường hợp phổ biến là một giá trị (chẳng hạn như 0) có giá trị cho ứng dụng đang được xử lý như falsevậy, do đó bị loại bỏ để sử dụng dự phòng không hợp lệ thay thế. Tuy nhiên - hãy xem câu trả lời của tôi WRT ngôn ngữ lập trình Biểu tượng, có thể có các biểu thức như thế nào get1() else get2() else default.
Steve314

1

Công tắc tổng quát đã nói ở trên:

 switch(x){
  predicate1:
     dosomething();
  predicate2:
     dosomethingelse();
 }

Trong Haskell:

  switch' :: a -> [(a -> Bool, b)] -> b
  switch' a [] = undefined
  switch' a (f,b):xs = if f a
                     then b
                      else switch' a xs

0

Trong C # tôi muốn sử dụng đơn giản switch () { ... }, nhưng có thể mở rộng với các biểu thức như vậy:

switch (value)
{
  // string-based operators:
  case begins "Maria": // to catch Maria Carey
    break;
  case ends "Washington": // to catch George Washington
    break;
  case like "ph": // to catch Phil, Phillip, Sophie
    break;
  case between "Aaron" and "April": // to catch all names between
    break;

  // use non-static variables in case expression:
  case Dao.GetDefaultBabyName():
    break;

  // continuable cases without breaking
  case "John":
    bonus = 25;
  case "Peter":
    salary = 500;
    break;

  // jumps between cases
  case "Aleron":
    // do something
    break;
  case "Bella":
    // do something
    jump "Aleron";
    break;

}

Và như thế. Cùng với những con số hoặc các loại khác (hỗ trợ mà IComparable, IConvertible...)

Điều này có thể làm cho mã của tôi trở nên gọn gàng và dễ đọc hơn.


Rơi vào các vụ án là một tội ác đã biết, và tôi nói chung là ổn nếu không có nó. Và nhảy là quá GOTOish cho bất kỳ chương trình lành mạnh. Nhưng có biểu hiện và phi thống kê trong trường hợp sẽ là một bổ sung đáng yêu.
CodexArcanum

0

Đó là một câu hỏi thú vị, như @Macneil nói.

Cấu trúc kiểm soát bất thường yêu thích của tôi, mà tôi (khiêm tốn ho) phát hiện ra, là thực thi khác biệt .

Nó có công dụng nhất định. Đối với tôi, việc sử dụng áp đảo là trong lập trình giao diện người dùng, đây là một ví dụ của vấn đề chung hơn về việc duy trì dữ liệu dư thừa tương ứng. Một mặt, có dữ liệu ứng dụng và mặt khác, có các điều khiển UI, cần phải được giữ trong thỏa thuận. Điều này nghe có vẻ như "ràng buộc" nhưng thực sự có nhiều hơn thế.

Thông thường tôi thực hiện nó bằng các macro trong C hoặc C ++. Trong C # tôi phải làm điều đó bằng các câu lệnh mở rộng bằng tay. Đó là một nỗi đau, nhưng nó hoạt động.

Khi tôi triển khai nó dưới dạng macro Lisp, và sau đó nó rất sạch sẽ. Nó không đòi hỏi sự cẩn thận từ phía lập trình viên. Tôi có thể đã làm điều tương tự trong bất kỳ ngôn ngữ có cấu trúc nào khác nếu tôi gặp khó khăn khi viết một trình phân tích cú pháp hoàn chỉnh và sau đó tạo ra tất cả các công cụ phù hợp. Đó là một dự án lớn, và tôi đã không thực hiện nó.


0

Các cấu trúc kiểm soát "truyền thống" giống như forvề việc kiểm soát người đàn ông làm việc, giữ cho anh ta kiên định với các hệ tư tưởng tham nhũng của giới tinh hoa tư bản cầm quyền. Đó là lý do tại sao tôi sử dụng các cấu trúc điều khiển thay thế như ph0rthay thế. Nó giống như for, nhưng triệt để hơn: Bạn sẽ không bắt gặp ph0rmặc một bộ đồ và cà vạt, phun ra một số BS công ty. ph0rgiữ cho nó thật, anh bạn

Chiến đấu chống lại quyền lực!


0

forVòng lặp đơn giản nhất-

for(100)
{
    //Will run for 100 times
}


for(i)
{
    //Will run for i times while i must be a positive integer
}


for(i as a)
{
    //Will run for i times while i must be a positive integer
    //and a is the incremental loop variable starting from 0 and 
    //scoped within the loop
}


for(i as a=2)
{
    //Will run for i times while i must be a positive integer
    //and a is the incremental loop variable starting from 2 and 
    //scoped within the loop
}
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.