2010-03-22 44 views
9

Tôi đang xác định lại phương thức trong đối tượng bằng ruby ​​và tôi cần phương thức mới để đóng. Ví dụ:Xác định phương thức đóng trong Ruby

def mess_it_up(o) 
    x = "blah blah" 

    def o.to_s 
    puts x # Wrong! x doesn't exists here, a method is not a closure 
    end 
end 

Bây giờ nếu tôi xác định một Proc, đó là một kết thúc:

def mess_it_up(o) 
    x = "blah blah" 

    xp = Proc.new {|| 
    puts x # This works 
    end 

    # but how do I set it to o.to_s. 

    def o.to_s 
    xp.call # same problem as before 
    end 
end 

Bất kỳ ý tưởng làm thế nào để làm điều đó?

Cảm ơn.

Trả lời

23

này hoạt động (thử nghiệm trong IRB):

Chú ý: Đây chỉ thay đổi str - không phải tất cả các trường hợp của String. Đọc dưới đây để biết chi tiết là tại sao các công trình này

another_str = "please don't change me!" 
str =   "ha, try to change my to_s! hahaha!" 
proc = Proc.new { "take that, Mr. str!" } 

singleton_class = class << str; self; end 

singleton_class.send(:define_method, :to_s) do 
    proc.call 
end 

puts str.to_s   #=> "take that, Mr. str!" 
puts another_str.to_s #=> "please don't change me!" 

# What! We called String#define_method, right? 

puts String   #=> String 
puts singleton_class #=> #<Class:#<String:0x3c788a0>> 

# ... nope! singleton_class is *not* String 
# Keep reading if you're curious :) 

này hoạt động bởi vì bạn đang mở str của singleton class và xác định một phương pháp đó. Bởi vì điều này, cũng như các cuộc gọi đến Module#define_method, có những gì một số gọi là "phạm vi bằng phẳng", bạn có thể truy cập các biến nằm ngoài phạm vi nếu bạn sử dụng def to_s; 'whatever'; end.

Bạn có thể muốn kiểm tra một số trong những "phép thuật lập trình meta" khác tại đây:

media.pragprog.com/titles/ppmetr/spells.pdf


Tại sao nó chỉ thay đổi str?

Vì Ruby có một vài thủ thuật thú vị lên đó là tay áo. Trong mô hình đối tượng Ruby, một phương thức gọi kết quả trong bộ thu không chỉ tìm kiếm lớp (và nó là tổ tiên), mà còn là lớp đơn (hoặc như Matz gọi nó là eigenclass). Lớp singleton này cho phép bạn định nghĩa một phương thức cho một đối tượng đơn lẻ. Những phương pháp này được gọi là "phương pháp singleton". Trong ví dụ trên, chúng ta đang làm điều đó - định nghĩa một tên phương thức singleton to_s. Đó là functionaly giống hệt như thế này:

def str.to_s 
    ... 
end 

Sự khác biệt duy nhất là chúng tôi có được sử dụng một đóng cửa khi gọi Module#define_method, trong khi def là một từ khóa, mà kết quả trong một sự thay đổi phạm vi.

Tại sao nó không đơn giản hơn?

Vâng, những tin tức tốt lành là bạn đang lập trình trong Ruby, vì vậy cảm thấy tự do để phát điên:

class Object 
    def define_method(name, &block) 
    singleton = class << self; self; end 
    singleton.send(:define_method, name) { |*args| block.call(*args) } 
    end 
end 


str = 'test' 
str.define_method(:to_s) { "hello" } 
str.define_method(:bark) { "woof!" } 
str.define_method(:yell) { "AAAH!" } 

puts str.to_s #=> hello 
puts str.bark #=> woof! 
puts str.yell #=> AAAH! 

Và, nếu bạn tò mò ...

Bạn biết phương pháp lớp học? Hoặc, trong một số ngôn ngữ, chúng ta sẽ gọi chúng là phương pháp tĩnh? Vâng, những người không thực sự tồn tại trong Ruby. Trong Ruby, các phương thức lớp thực sự chỉ là các phương thức được định nghĩa trong lớp đơn lớp của đối tượng Class.

Nếu điều đó nghe có vẻ điên rồ, hãy xem các liên kết tôi đã cung cấp ở trên. Rất nhiều sức mạnh của Ruby chỉ có thể được khai thác nếu bạn biết làm thế nào để metaprogram - trong trường hợp đó bạn sẽ thực sự muốn biết về các lớp/phương thức singleton, và nói chung, mô hình đối tượng Ruby.

HTH

Charles

+0

Nhưng tôi cần phải sửa đổi nó chỉ trong một đối tượng, không phải trong tất cả (trong lớp). – Pablo

+0

Đó là những gì nó làm. Mỗi đối tượng có những gì được gọi là một lớp singleton - đó là lớp riêng của cá nhân để lưu trữ các phương thức. Kiểm tra nó ra - bạn sẽ thấy rằng các trường hợp khác của String không bị ảnh hưởng. Tôi sẽ cập nhật câu trả lời của tôi với một bài kiểm tra, để cho thấy rằng nó hoạt động. – Charles

+0

Ồ, sai lầm của tôi, tôi sẽ thử. – Pablo

0

Điều này dường như hoạt động.

class Foo 
    def mess_it_up(o) 
    x = "blah blah" 

    o.instance_variable_set :@to_s_proc, Proc.new { puts x } 
    def o.to_s 
     @to_s_proc.call 
    end 
    end 
end 

var = Object.new 
Foo.new.mess_it_up(var) 

var.to_s 

Vấn đề là mã trong def không được đánh giá cho đến khi nó chạy và trong phạm vi mới. Vì vậy, bạn phải lưu khối vào một biến mẫu trên đối tượng đầu tiên và lấy nó sau này.

define_method không hoạt động vì đó là phương pháp lớp, nghĩa là bạn sẽ phải gọi nó trên lớp đối tượng của bạn, cung cấp mã đó cho TẤT CẢ trường hợp của lớp đó chứ không chỉ trường hợp này.

+0

Không, bạn muốn sử dụng 'module # define_method' trên lớp singleton của đối tượng - trong trường hợp này chỉ có một đối tượng được thay đổi. Chạy ví dụ trong câu trả lời của tôi nếu bạn nghi ngờ tôi :) – Charles

+1

Yep câu trả lời của bạn tốt hơn. Tôi quên về lớp singleton. –

9

Feature #1082 thực hiện trong Ruby 1.9.2 làm cho điều này là một nhiệm vụ dễ dàng với Object#define_singleton_method:

def mess_it_up(o) 
    x = "blah blah" 

    # Use Object#define_singleton_method to redefine `to_s' 
    o.define_singleton_method(:to_s) { x } 
end 

Các khái niệm có liên quan vẫn còn giống như trong tôi previous answer, mà cung cấp mô tả chi tiết hơn về cách thức hoạt động trong mô hình đối tượng của Ruby, cũng như định nghĩa Object#define_method có khái niệm giống như của Ruby 1.9.2 Object#define_singleton_method.

Các phương pháp khác mà bạn có thể thấy hữu ích cho các nhiệm vụ tương tự:

+0

Tôi đã phải sử dụng điều này, khi tôi muốn để tạo ra một phương thức cá thể để tùy chỉnh một đối tượng bằng cách thêm các cuộc gọi vào các phương thức của cá thể tạo ra: 'class A; def foo (b); đặt "fooing # {b}"; kết thúc; def agument (b); b.define_singleton_class (: foo) {foo (tự); }; kết thúc; kết thúc'. Điều này rõ ràng là không hiệu quả, bởi vì không có cách kỳ diệu nào để tham chiếu đến 'tự ngã' nằm trong phạm vi của kết thúc. Giải pháp là lấy một trang ra khỏi cuốn sách của Javascript và làm một cái gì đó kỳ lạ như: 'bản thân mình = tự' và sau đó sử dụng' bản thân mình' trong phương thức singleton để gọi cho phụ huynh. Nghe có vẻ hợp lý? – Guss

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