2012-01-06 38 views
6

Nền của tôi nằm trong PHP và C#, nhưng tôi thực sự muốn tìm hiểu về RoR. Để kết thúc, tôi đã bắt đầu đọc tài liệu chính thức. Tôi có một số câu hỏi về một số ví dụ về mã.Một số câu hỏi đơn giản của Ruby - trình vòng lặp, khối và ký hiệu

Đầu tiên là với vòng lặp:

class Array 
    def inject(n) 
    each { |value| n = yield(n, value) } 
    n 
    end 

    def sum 
    inject(0) { |n, value| n + value } 
    end 

    def product 
    inject(1) { |n, value| n * value } 
    end 
end 

Tôi hiểu rằng yield có nghĩa là "thực hiện khối liên quan đến đây." Việc ném tôi là phần |value| n = của each. Các khối khác có ý nghĩa hơn đối với tôi khi chúng dường như bắt chước C# style lambdas:

public int sum(int n, int value) 
{ 
    return Inject((n, value) => n + value); 
} 

Nhưng ví dụ đầu tiên gây nhầm lẫn cho tôi.

Cách khác là có biểu tượng. Khi nào tôi muốn sử dụng chúng? Và tại sao tôi không thể làm điều gì đó như:

class Example 
    attr_reader @member 

    # more code 
end 

Trả lời

3

Trong phương pháp inject or reduce, n đại diện cho một giá trị tích lũy; điều này có nghĩa là kết quả của mỗi lần lặp lại được tích lũy trong biến số n. Điều này có thể, như trong ví dụ của bạn, tổng hoặc sản phẩm của các phần tử trong mảng.

yield trả về kết quả của khối, được lưu trữ trong n và được sử dụng trong các lần lặp tiếp theo. Đây là những gì làm cho kết quả "tích lũy".

a = [ 1, 2, 3 ] 
a.sum # inject(0) { |n, v| n + v } 
# n == 0; n = 0 + 1 
# n == 1; n = 1 + 2 
# n == 3; n = 3 + 3 
=> 6 

Ngoài ra, để tính tổng số bạn cũng có thể viết a.reduce :+. Điều này làm việc cho bất kỳ hoạt động nhị phân nào. Nếu phương pháp của bạn có tên là symbol, viết a.reduce :symbol cũng giống như viết a.reduce { |n, v| n.symbol v }.

attr và công ty thực sự là phương pháp. Dưới mui xe, họ tự động xác định các phương pháp cho bạn. Nó sử dụng biểu tượng bạn đã truyền để tìm ra tên của biến cá thể và các phương thức. :member kết quả trong biến số @member và các phương thức membermember =.

Lý do bạn không thể viết attr_reader @member là bởi vì @member không phải là một đối tượng trong chính nó, cũng như không thể chuyển đổi thành biểu tượng; nó thực sự nói với ruby ​​để lấy giá trị của biến cá thể @member của đối tượng self, trong đó, ở phạm vi lớp, là chính lớp đó.

Để minh họa:

class Example 
    @member = :member 
    attr_accessor @member 
end 

e = Example.new 
e.member = :value 
e.member 
=> :value 

Hãy nhớ rằng việc tiếp cận các biến dụ unset mang nil, và kể từ khi gia đình attr phương pháp duy nhất chấp nhận biểu tượng, bạn nhận được: TypeError: nil is not a symbol.

Về việc sử dụng Symbol, bạn có thể loại sử dụng chúng như chuỗi. Chúng tạo ra các khóa băm tuyệt vời vì các ký hiệu bằng nhau luôn tham chiếu đến cùng một đối tượng, không giống như các chuỗi.

:a.object_id == :a.object_id 
=> true 
'a'.object_id == 'a'.object_id 
=> false 

Chúng cũng thường được sử dụng để tham chiếu đến tên phương thức và can actually be converted to Procs, có thể được chuyển đến phương pháp. Đây là những gì cho phép chúng tôi viết những thứ như array.map &:to_s.

Khám phá this article để hiểu thêm về biểu tượng.

1

Đối với định nghĩa của inject, bạn về cơ bản thiết lập khối xích. Cụ thể, biến n trong số {|value| n = yield(n, value)} về cơ bản là bộ tích lũy cho khối được chuyển đến inject. Vì vậy, ví dụ, đối với định nghĩa của product, inject(1) {|value| n * value}, giả sử bạn có một mảng my_array = [1, 2, 3, 4]. Khi bạn gọi my_array.product, bạn bắt đầu bằng cách gọi inject với n = 1. each sản lượng cho khối được xác định trong inject, lần lượt tạo thành khối được tự động chuyển đến inject với n (1) và giá trị đầu tiên trong mảng (1 tốt, trong trường hợp này). Khối này, {|n, value| n * value} trả về 1 == 1 * 1, được đặt là biến số n inject. Tiếp theo, 2 được sinh ra từ mỗi khối, và khối được xác định trong inject sản lượng khối là yield(1, 2), trả về 2 và gán nó thành n. Tiếp theo 3 được lấy từ each, khối tạo ra các giá trị (2, 3) và trả về 6, được lưu trữ trong n cho giá trị tiếp theo, v.v. Về cơ bản, theo dõi giá trị tổng thể bất khả tri của việc tính toán được thực hiện trong các thói quen chuyên ngành (sumproduct) cho phép tổng quát hóa. Không có điều đó, bạn phải khai báo, ví dụ:

def sum 
    n = 0 
    each {|val| n += val} 
end 

def product 
    n = 1 
    each {|val| n *= val} 
end 

gây khó chịu lặp đi lặp lại.

Đối với câu hỏi thứ hai của bạn, attr_reader và gia đình của chính họ là các phương pháp xác định các quy trình truy cập thích hợp sử dụng define_method nội bộ, trong một quá trình được gọi là lập trình meta; chúng không phải là ngôn ngữ, mà chỉ là những phương pháp cũ đơn giản. Các hàm này mong đợi để truyền một biểu tượng (hoặc, có lẽ, một chuỗi) cung cấp tên của các trình truy cập bạn đang tạo. Theo lý thuyết, bạn có thể sử dụng các biến mẫu như @member ở đây, mặc dù nó sẽ là giá trị mà tại đó @member điểm sẽ được chuyển vào và được sử dụng trong define_method. Để biết ví dụ về cách chúng được triển khai, this page hiển thị một số ví dụ về phương thức attr_ *.

1
def inject(accumulator) 
    each { |value| accumulator = yield(accumulator, value) } 
    accumulator 
end 

Đây chỉ là giá trị hiện tại của accumulator và mục mảng để chèn khối và sau đó lưu lại kết quả vào bộ đếm.

class Example 
    attr_reader @member 
end 

attr_reader chỉ là phương pháp có đối số là tên của người truy cập bạn muốn thiết lập.Vì vậy, theo một cách giả tạo bạn có thể làm

class Example 
    @ivar_name = 'foo' 
    attr_reader @ivar_name 
end 

để tạo ra một phương thức getter gọi foo

1

Sự nhầm lẫn của bạn với ví dụ đầu tiên có thể là do bạn đọc |value| n dưới dạng một biểu thức, nhưng không phải.

phiên bản định dạng lại này có thể rõ ràng hơn cho bạn:

def inject(n) 
    each do |value| 
    n = yield(n, value) 
    end 
    return n 
end 

value là một phần tử trong mảng, và nó được mang lại với n để bất cứ điều gì khối được truyền cho inject, kết quả trong số đó được thiết lập để n .Nếu không rõ, hãy đọc lên phương thức each, sẽ mất một khối và tạo ra từng mục trong mảng đó. Sau đó, nó sẽ được rõ ràng hơn như thế nào tích lũy hoạt động.

attr_reader là ít lạ khi bạn cho rằng đó là phương pháp tạo phương thức truy cập. Nó không phải là một accessor trong chính nó. Nó không cần phải xử lý giá trị của biến số @member, chỉ là tên của nó. :member chỉ là phiên bản nội bộ của chuỗi 'thành viên', là tên của biến.

Bạn có thể nghĩ biểu tượng là chuỗi trọng lượng nhẹ hơn, với phần thưởng bổ sung là mỗi nhãn bằng nhau là cùng một đối tượng - :foo.object_id == :foo.object_id, trong khi 'foo'.object_id != 'foo'.object_id, vì mỗi 'foo' là một đối tượng mới. Bạn có thể thử điều đó cho chính mình trong irb. Hãy coi chúng như là nhãn, hoặc dây nguyên thủy. Chúng hữu ích đáng kinh ngạc và xuất hiện rất nhiều, ví dụ: cho metaprogramming hoặc như các phím trong băm. Như được chỉ ra ở nơi khác, gọi số object.send :foo giống như gọi object.foo

Có thể đọc một số chương sớm từ 'pickaxe' book để tìm hiểu thêm ruby, nó sẽ giúp bạn hiểu và đánh giá cao các công cụ bổ sung.

0

Trước tiên, bạn cần phải hiểu nơi để sử dụng các biểu tượng và vị trí của nó không .. Biểu tượng được sử dụng đặc biệt để đại diện cho một cái gì đó. Ví dụ:: name,: tuổi như thế. Ở đây chúng ta sẽ không thực hiện bất kỳ hoạt động nào bằng cách sử dụng nó. Chuỗi chỉ được sử dụng để xử lý dữ liệu. Ví dụ: 'a = name'. Ở đây tôi sẽ sử dụng biến này 'a' hơn nữa cho các hoạt động chuỗi khác trong ruby. Hơn nữa, biểu tượng là bộ nhớ hiệu quả hơn dây và nó là không thay đổi. Đó là lý do tại sao nhà phát triển ruby ​​thích các biểu tượng hơn là chuỗi.

Bạn thậm chí có thể sử dụng phương pháp bơm để tính tổng như (1..5) .to_a.inject (: +)

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