2010-10-26 30 views
8

Có ai lý do tại sao tôi có thể viết này:của Ruby trong khi cú pháp

ruby-1.8.7-p302 > a = %w(a b c) 
=> ["a", "b", "c"] 
ruby-1.8.7-p302 > while (i = a.shift) do; puts i ; end 
a 
b 
c 
=> nil 

nào trông giống như đi qua một khối thời gian. Và không:

while(i = a.shift) { puts i; } 

Có phải vì sự "làm" của cú pháp khi được chỉ đường syntaxic và như không có gì để làm với "làm" của một khối?

+0

Câu hỏi hay! –

Trả lời

12

Có phải vì các do của cú pháp while chỉ là đường syntaxic và như không có gì để làm với do của một khối?

Ít hoặc nhiều, có. Đó không phải là cú pháp, nó chỉ đơn giản là một cấu trúc ngôn ngữ dựng sẵn, như def hoặc class, như @meagar đã viết.

Không có gì liên quan đến do của một khối, ngoại trừ từ khóa đó là tốn kém và vì vậy việc sử dụng lại từ khóa có ý nghĩa. (Theo "đắt" Tôi có nghĩa là họ hạn chế các lập trình viên trong biểu cảm của mình.)

Trong một vòng lặp while, có hai cách để tách các khối từ các điều kiện:

  1. các do từ khóa và
  2. dấu tách biểu thức.

Có, đến lượt nó, hai dải phân cách khác nhau biểu hiện trong Ruby:

  1. dấu chấm phẩy ;
  2. một dòng mới

Vì vậy, cả ba điều nào sau đây là hợp lệ:

while i = a.shift do puts i end # do 

while i = a.shift; puts i end # semicolon 

while i = a.shift 
    puts i end     # newline 

[Obvi ously, người cuối cùng sẽ không được viết theo cách đó, bạn sẽ đặt end trên một dòng mới, được khấu trừ để khớp với while. Tôi chỉ muốn chứng minh mức tối thiểu cần thiết để tách các bộ phận của vòng while là gì.]

Nhân tiện: nó rất không thành ngữ để đặt điều kiện trong dấu ngoặc đơn. Ngoài ra còn có rất nhiều dấu chấm phẩy thừa trong mã của bạn. Và tên biến số i thường được dành riêng cho một chỉ mục, không phải là một phần tử. (Tôi thường sử dụng el cho các yếu tố chung, nhưng tôi thích nhiều tên ngữ nghĩa hơn.)

Nó cũng rất không thành ngữ để lặp lại một bộ sưu tập theo cách thủ công.Mã của bạn sẽ được nhiều hơn bằng văn bản như

a.each(&method(:puts)).clear 

Không chỉ là nó dễ dàng hơn để hiểu những gì điều này (in tất cả các yếu tố của mảng và xóa tất cả các mục từ nó), nó cũng là dễ dàng hơn nhiều để viết (có không có cách nào để có được điều kiện chấm dứt sai, hoặc vít lên bất kỳ bài tập nào). Điều này cũng sẽ hiệu quả hơn: phiên bản của bạn là Θ (n), phiên bản này là Θ (n).

Và thực tế, đó không thực sự là cách bạn sẽ viết nó, vì, vì dù sao, Kernel#puts cũng đã thực hiện hành vi đó. Vì vậy, những gì bạn sẽ thực sự ghi là này

puts a 
a.clear 

hoặc có lẽ đây

a.tap(&method(:puts)).clear 

[Lưu ý: một cuối cùng này không phải là 100% tương đương. Nó in một dòng mới cho một mảng trống, tất cả những cái khác không in được gì.]

Đơn giản. Thông thoáng. Ngắn gọn. Biểu cảm. Nhanh.

so sánh với:

while (i = a.shift) do; puts i ; end 

Tôi thực sự đã phải chạy mà nhiều lần để được 100% rõ ràng những gì nó làm.

+0

Rực rỡ. Tôi phải đánh dấu và nghiên cứu cái này. :) –

+1

Phương pháp # to_proc ... Tôi nghĩ rằng tôi chỉ yêu bạn một chút jorg –

+0

câu trả lời tuyệt vời, cảm ơn rất nhiều Jörg! – marcgg

6

while không có khối, đó là ngôn ngữ xây dựng. Các do là không bắt buộc:

while (i = a.shift) 
    puts i 
end 
Các vấn đề liên quan