2011-02-06 33 views
33

Giải thích tốt nhất cho các khối Ruby mà bạn có thể chia sẻ là gì?Giải thích tốt nhất về khối Ruby?

Cả mã sử dụng và mã có thể bị chặn?

+1

Bạn đang tìm kiếm giới thiệu về khái niệm về các khối hoặc tham chiếu đầy đủ về chúng? – Phrogz

+16

Hoặc bạn chỉ đang trolling cho đại diện bằng cách đặt câu hỏi mà bạn không cần câu trả lời cho, không có ý định chấp nhận, và không có ý định tham gia vào cuộc thảo luận? Chúng tôi sẽ xem bạn có trả lời không. – Phrogz

+0

Đây là một chuỗi hữu ích: http://www.reactive.io/tips/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/ – Lucio

Trả lời

6

Cuốn sách "Programming Ruby" có số lượng lớn explanation of blocks and using them.

Trong 1.9+, danh sách tham số được truyền vào một khối trở nên phức tạp hơn, cho phép các biến địa phương được xác định:

do |a,b;c,d| 
    some_stuff 
end 

;c,d tuyên bố hai biến cục bộ mới bên trong khối, mà không nhận được giá trị từ tuyên bố yield được gọi là thường lệ. Ruby 1.9+ đảm bảo rằng, nếu các biến tồn tại bên ngoài khối, chúng sẽ không bị dồn bởi các biến có cùng tên trong khối. Đây là hành vi mới; 1.8 sẽ dẫm lên chúng.

def blah 
    yield 1,2,3,4 
end 

c = 'foo' 
d = 'bar' 

blah { |a, *b; c,d| 
    c = 'hello' 
    d = 'world' 
    puts "a: #{a}", "b: #{b.join(',')}", "c: #{c}", "d: #{d}" 
} 

puts c, d 
# >> a: 1 
# >> b: 2,3,4 
# >> c: hello 
# >> d: world 
# >> foo 
# >> bar 

Ngoài ra còn có "splat" hành *, mà làm việc trong danh sách các thông số:

do |a,*b| 
    some_stuff 
end 

sẽ gán là người đầu tiên của nhiều giá trị để "a", và tất cả các phần còn lại sẽ được chụp trong "b" sẽ được xử lý như một mảng. Các * có thể nằm trên biến a:

do |*a,b| 
    some_stuff 
end 

sẽ nắm bắt tất cả trôi qua trong các biến ngoại trừ người cuối cùng, trong đó sẽ được chuyển tới b. Và, tương tự như trước đó hai:

do |a,*b,c| 
    some_stuff 
end 

sẽ gán giá trị đầu tiên a, giá trị cuối cùng để c và tất cả/bất kỳ giá trị can thiệp để b.

Tôi nghĩ điều đó khá mạnh mẽ và bóng bẩy.

Ví dụ:

def blah 
    yield 1,2,3,4 
end 

blah { |a, *b| puts "a: #{a}", "b: #{b.join(',')}" } 
# >> a: 1 
# >> b: 2,3,4 

blah { |*a, b| puts "a: #{a.join(',')}", "b: #{b}" } 
# >> a: 1,2,3 
# >> b: 4 

blah { |a, *b, c| puts "a: #{a}", "b: #{b.join(',')}", "c: #{c}" } 
# >> a: 1 
# >> b: 2,3 
# >> c: 4 
19

Từ Why's (poignant) guide to ruby:

Bất kỳ mã bao quanh bởi dấu ngoặc nhọn là một khối.

2.times { print "Yes, I've used chunky bacon in my examples, but never again!" } là ví dụ.

Với các khối, bạn có thể nhóm một tập hợp các hướng dẫn với nhau để họ có thể được chuyển quanh chương trình của bạn. Niềng răng xoăn cung cấp cho sự xuất hiện của con cua cua đã giật mã và đang giữ nó lại với nhau. Khi bạn thấy hai kìm này, hãy nhớ rằng mã bên trong đã được nhấn vào một đơn vị duy nhất.Nó giống như một trong những những ít hộp Hello Kitty họ bán tại trung tâm mua sắm đó là nhồi với bút chì nhỏ và giấy nhỏ li ti, tất cả nhồi nhét vào một lấp lánh hộp trong suốt có thể được giấu trong lòng bàn tay của bạn cho bí mật stationary hoạt động. Ngoại trừ các khối không đòi hỏi quá nhiều nheo mắt. Các dấu ngoặc nhọn cũng có thể được giao dịch cho các từ và kết thúc, điều này thật tuyệt nếu khối của bạn dài hơn một dòng.

loop do 
    print "Much better."  
    print "Ah. More space!" 
    print "My back was killin' me in those crab pincers." 
end 

đối số khối là một tập hợp của các biến bao quanh bởi ống nhân vật và cách nhau bằng dấu phẩy.

|x|, |x,y|, and |up, down, all_around| are examples. 

đối số khối được sử dụng vào đầu của một khối.

{ |x,y| x + y } 

Trong ví dụ trên, | x, y | là các đối số. Sau các đối số, chúng tôi có một chút mã. Biểu thức x + y thêm hai đối số vào nhau. Tôi muốn suy nghĩ về các ký tự ống làm đại diện cho đường hầm. Họ cung cấp cho sự xuất hiện của một máng mà các biến số đang trượt xuống. (An x đi xuống đại bàng lây lan, trong khi y gọn gàng vượt qua chân của mình.) Chute này hoạt động như một lối đi giữa các khối và thế giới xung quanh họ . Các biến được đi qua rãnh này (hoặc đường hầm) vào khối.

+16

"Bất kỳ mã nào được bao quanh bởi dấu ngoặc nhọn là một khối" trừ khi nó là một ** băm **. – Meltemi

+1

Bạn không giải thích những gì các ví dụ này trở lại. Tôi không hiểu. –

+0

Hãy làm gia sư của tôi! Cảm ơn bạn đã giải thích nó theo cách đơn giản và rõ ràng. – Benjamints

3

Chặn là cách nhóm mã trong Ruby. Có hai cách để viết các khối. Một là sử dụng câu lệnh do..end và cái kia bao quanh mã trong các dấu ngoặc nhọn: {}. Các khối được coi là các đối tượng trong ngôn ngữ lập trình Ruby, và theo mặc định, tất cả các hàm chấp nhận một đối số khối ẩn.

Dưới đây là hai ví dụ về khối làm điều tương tự:

 
2.times { puts 'hi' } 
2.times do 
    puts 'hi' 
end 

Blocks có thể nhận được danh sách các đối số bằng dấu phẩy bên trong thanh dọc ||. Ví dụ:

 
[1,2].map{ |n| n+2 } # [3, 4] 

Blocks (trong ruby ​​1.9.2) rõ ràng có thể có các biến địa phương:

 
x = 'hello' 
2.times do |;x| 
    x = 'world' 
    puts x 
end 

=> world 
=> world 

biến địa phương có thể được kết hợp với các thông số:

 
[1,2].map{ |n;x| n+2 } 

Tất cả các chức năng có thể nhận được đối số khối mặc định:

 
def twice 
    yield 
    yield 
end 

twice { puts 'hello' } 
=> hello 
=> hello 

Sự khác nhau giữa các ký tự..end và {} là gì? Theo quy ước {} khối là trên một dòng và làm ..các khối kết thúc bao trùm nhiều dòng, vì chúng dễ đọc hơn theo cách này. Sự khác biệt chính phải làm với ưu tiên là:

 
array = [1,2] 

puts array.map{ |n| n*10 } # puts (array.map{ |n| n*10 }) 
=> 10 
=> 20 

puts array.map do |n| n*10 end # (puts array.map) do |n| n*10 end 
=> <Enumerator:0x00000100862670> 
2

Khối là chữ viết tắt cho quy trình hạng nhất ẩn danh với một số giới hạn gây phiền nhiễu. Họ làm việc cùng một cách trong Ruby khi họ làm việc trong khá nhiều mọi ngôn ngữ lập trình khác, modulo những hạn chế vện đã đề cập, đó là:

  • khối chỉ có thể xuất hiện trong danh sách đối số
  • nhiều nhất là một khối có thể xuất hiện trong một danh sách đối số (và nó phải là đối số cuối cùng)
+0

Câu trả lời hay nhưng mối quan hệ với đối tượng Proc có vẻ cần thiết, phải không? – maerics

+0

@maerics Cần thiết cho một tài nguyên đầy đủ trên các khối? Vâng. Cần thiết để giải thích các khối (mà tôi giải thích như là một giới thiệu cho họ cho người mới)? Chắc chắn là không, IMO. – Phrogz

+0

Cảm ơn. Của bạn là câu trả lời duy nhất giúp tôi hiểu tại sao '{puts" hello "}' không hoạt động. Không được phép? Lạ nhỉ. –

27

tôi cung cấp lên lời giải thích của riêng tôi từ this answer, chút thay đổi:

"Blocks" trong Ruby là không giống như các thuật ngữ lập trình nói chung " khối mã "o r "khối mã".

Giả vờ cho một khoảnh khắc đó như sau (không hợp lệ) mã Ruby làm việc thực tế:

def add10(n) 
    puts "#{n} + 10 = #{n+10}" 
end 

def do_something_with_digits(method) 
    1.upto(9) do |i| 
    method(i) 
    end 
end 

do_something_with_digits(add10) 
#=> "1 + 10 = 11" 
#=> "2 + 10 = 12" 
... 
#=> "9 + 10 = 19" 

Trong khi mã đó là không hợp lệ, nó ý định đi qua một số mã đến một phương pháp và có phương pháp mà chạy mã là có thể trong Ruby theo nhiều cách khác nhau. Một trong những cách đó là "Chặn".

Một khối trong Ruby rất, rất giống một phương thức: có thể lấy một số đối số và chạy mã cho các đối tượng đó. Bất cứ khi nào bạn nhìn thấy foo{ |x,y,z| ... } hoặc foo do |x,y,z| ... end, đó là các khối có ba tham số và chạy ... trên chúng. (Bạn thậm chí có thể thấy rằng phương pháp upto trên đã được thông qua một khối.)

Vì Blocks là một phần đặc biệt của cú pháp Ruby, mọi phương pháp được cho phép để được thông qua một khối. Có hay không phương pháp sử dụng khối là tùy thuộc vào phương pháp. Ví dụ:

def say_hi(name) 
    puts "Hi, #{name}!" 
end 

say_hi("Mom") do 
    puts "YOU SUCK!" 
end 
#=> Hi, Mom! 

Phương thức trên được chuyển qua một khối sẵn sàng phát hành xúc phạm, nhưng vì phương pháp này không bao giờ gọi khối, chỉ có thông điệp đẹp được in. Dưới đây là cách chúng tôi gọi khối từ phương thức:

def say_hi(name) 
    puts "Hi, #{name}!" 
    if block_given? 
    yield(name) 
    end 
end 

say_hi("Mridang") do |str| 
    puts "Your name has #{str.length} letters." 
end 
#=> Hi, Mridang! 
#=> Your name has 7 letters. 

Chúng tôi sử dụng block_given? để xem liệu khối có được truyền hay không. Trong trường hợp này, chúng tôi đã thông qua một đối số trở lại khối; nó tùy thuộc vào phương pháp của bạn để quyết định những gì để vượt qua khối. Ví dụ:

def say_hi(name) 
    puts "Hi, #{name}!" 
    yield(name, name.reverse) if block_given? 
end 

say_hi("Mridang"){ |str1, str2| puts "Is your name #{str1} or #{str2}?" } 
#=> Hi, Mridang! 
#=> Is your name Mridang or gnadirM? 

Đây chỉ là quy ước (và tốt nhất bạn muốn hỗ trợ) cho một số lớp để chuyển thể hiện vừa tạo cho khối.

Đây không phải là câu trả lời đầy đủ, vì nó không bao gồm chụp khối làm đối số, cách xử lý tính chất, bỏ định dạng tham số khối, v.v ... nhưng dự định đóng vai trò giới thiệu Blocks-Are-Lambdas.

26

Khối Ruby là cách tạo Proc objects đại diện cho mã có thể được sử dụng bởi mã khác.Đối tượng Proc là hướng dẫn giữa các dấu ngoặc nhọn {} (hoặc do...end cụm từ cho các khối nhiều dòng, có ưu tiên thấp hơn dấu ngoặc nhọn), có thể tùy chọn lấy đối số và giá trị trả lại (ví dụ: {|x,y| x+y}). Procs là first-class objects và có thể được xây dựng một cách rõ ràng hoặc đạt ngầm như phương pháp giả lập luận:

  1. Xây dựng như một đối tượng Proc (hoặc sử dụng lambda từ khóa):

    add1 = Proc.new {|x| x+1} # Returns its argument plus one. 
    add1.call(1) # => 2 
    
  2. Qua như một giả phương pháp đối số, hoặc sử dụng rõ ràng toán tử đường cú pháp đối số cuối cùng đặc biệt & hoặc sử dụng ngầm một cặp block_given?/yield:

    def twice_do(&proc) # "proc" is the block given to a call of this method. 
        2.times { proc.call() } if proc 
    end 
    twice_do { puts "OK" } # Prints "OK" twice on separate lines. 
    
    def thrice_do() # if a block is given it can be called with "yield". 
        3.times { yield } if block_given? 
    end 
    thrice_do { puts "OK" } # Prints "OK" thrice on separate lines. 
    

Biểu mẫu thứ hai thường được sử dụng cho Visitor patterns; dữ liệu có thể được chuyển tới các đối số khối đặc biệt làm đối số cho các phương thức call hoặc yield.

+4

Niềng răng có ưu tiên cao; 'do' có ưu tiên thấp. Nếu lời gọi phương thức có các tham số không được đặt trong dấu ngoặc đơn, thì dạng cú đúp của một khối sẽ liên kết với tham số cuối cùng, chứ không phải với lời gọi tổng thể. Biểu mẫu 'do' sẽ liên kết với lời gọi. – Green

+0

Tại sao lại là downvote? – maerics

+2

Tiếng Anh, vui lòng! ...... "Ruby khối là cú pháp literals cho Proc đối tượng ...." - nếu mọi người không biết những gì một khối là, tôi đoán họ sẽ không biết những gì "cú pháp literals cho Proc đối tượng" có nghĩa là một trong hai . cố gắng giải thích như thể độc giả đã 5 tuổi. – BKSpurgeon

6

Đối với bất cứ ai đến với câu hỏi này từ một nền C# (hoặc langs khác thực sự), điều này có thể giúp:

của Ruby khối giống như biểu thức lambda và các phương pháp vô danh trong C#. Họ là những gì C# gọi đại biểu (và Ruby gọi Procs), đó là để nói rằng họ về cơ bản các chức năng có thể được thông qua như là giá trị. Trong cả Ruby và C#, chúng cũng có thể hoạt động như các bao đóng.

Ruby: { |x| x + 1 }

C#: x => x + 1

Ruby: { |name| puts "Hello there #{name}" }

C#: name => { Console.WriteLine("Hello there {0}", name); }

Cả C# và Ruby cung cấp những cách khác để viết các ví dụ trên.

Ruby:

do |name| 
    puts "Hello there #{name}" 
end 

C#:

delegate(string name) 
{ 
    Console.WriteLine("Hello there {0}", name); 
} 

Trong cả hai Ruby và C#, nhiều báo cáo được cho phép, Trong Ruby, cú pháp thứ hai ở trên là cần thiết cho việc này.

Các khái niệm này có sẵn bằng nhiều ngôn ngữ khác đã bị ảnh hưởng bởi những ý tưởng đằng sau lập trình hàm.

Các vấn đề liên quan