2009-09-13 29 views
8

Ruby xác định #clone trong Đối tượng. Để tôi ngạc nhiên, một số lớp nâng cao ngoại lệ khi gọi nó. tôi thấy NilClass, TrueClass, FalseClass, Fixnum có hành vi này.Lớp Ruby nào hỗ trợ .clone?

1) Có danh sách đầy đủ các lớp (ít nhất là lớp lõi) tồn tại, không cho phép #clone? Hoặc có cách nào để phát hiện xem một lớp học cụ thể có hỗ trợ #clone không?

2) Có gì sai với 42.clone?

+2

Tôi thực sự muốn biết làm thế nào bạn kiểm tra xem một lớp có nhân bản được không. Có vẻ như nếu một lớp không muốn cho phép bản thân được nhân bản thì nó sẽ làm cho riêng phương thức sao chép mà nó kế thừa từ Object để bạn có thể kiểm tra sự tồn tại của nó chỉ dưới public_methods. Có vẻ như thông thường với tôi. –

Trả lời

7

Tôi không nghĩ rằng có một danh sách chính thức, ít nhất là trừ khi bạn đếm đọc nguồn. Lý do 2) không hoạt động là do tối ưu hóa được áp dụng cho Fixnums. Chúng được lưu trữ/truyền trong nội bộ như các giá trị thực tế của chúng (như vậy là đúng, sai và không), và không phải là con trỏ. Các giải pháp ngây thơ là chỉ có 42.clone trả lại cùng một 42, nhưng sau đó bất biến obj.clone.object_id != obj.object_id sẽ không còn giữ, 42.clone sẽ không thực sự được nhân bản.

+0

'obj.clone.object_id! = Obj.object_id' là true và' obj. clone.object_id == obj.object_id 'không phải lúc nào cũng đúng là khác nhau. Đó là cựu không giữ không có nghĩa là sau này không. – sawa

0

Bạn không thể sao chép các lớp không thể thay đổi. I E. bạn có thể chỉ có một thể hiện của đối tượng 42 (như là một Fixnum), nhưng có thể có nhiều trường hợp "42" (vì chuỗi là có thể thay đổi). Bạn không thể sao chép các biểu tượng cũng như vì chúng là một cái gì đó giống như chuỗi bất biến.

Bạn có thể kiểm tra xem trong IRB với phương thức object_id. (biểu tượng và fixnums sẽ cung cấp cho bạn cùng một object_id sau khi các cuộc gọi lặp đi lặp lại)

+1

Tính không ổn định không liên quan gì đến nó (trên thực tế, bạn có thể thêm trạng thái vào Fixnum). – Chuck

+0

Hành vi mặc định thực sự kỳ lạ cho Fixnum, đặc biệt là vì nó có phương thức sao chép, d.class.method_defined? (: Clone) == true –

5

Fixnum là một lớp đặc biệt được xử lý đặc biệt theo ngôn ngữ. Từ thời điểm chương trình của bạn khởi chạy, có một Fixnum chính xác cho mọi số mà lớp đó có thể đại diện, và chúng được đưa ra một biểu diễn đặc biệt không chiếm nhiều không gian - theo cách này, toán học cơ bản không phân bổ và deallocating bộ nhớ như điên. Bởi vì điều này, không thể có nhiều hơn một 42.

Đối với những người khác, tất cả đều có một điểm chung: Họ là những người độc thân. Chỉ có một thể hiện của một lớp đơn theo định nghĩa, vì vậy cố gắng sao chép nó là một lỗi.

+1

"Bởi vì điều này, không thể có nhiều hơn một 42.". Và tại sao phải có? Thật hoàn hảo. –

1

tôi vẫn không biết làm thế nào để kiểm tra clonability đúng nhưng đây là một cách ác rất vụng về để kiểm tra clonablity sử dụng bẫy lỗi:

def clonable?(value) 
    begin 
    clone = value.clone 
    true 
    rescue 
    false 
    end 
end 

Và đây là cách bạn có thể sao chép ngay cả những unclonable. Ít nhất là cho rất ít lớp học tôi đã mệt mỏi với nó.

def super_mega_clone(value) 
    eval(value.inspect) 
end 

Dưới đây là một số thử nghiệm mẫu:

b = :b 
puts "clonable? #{clonable? b}" 

b = proc { b == "b" } 
puts "clonable? #{clonable? b}" 

b = [:a, :b, :c] 
c = super_mega_clone(b) 

puts "c: #{c.object_id}" 
puts "b: #{b.object_id}" 
puts "b == c => #{b == c}" 
b.each_with_index do |value, index| 
    puts "[#{index}] b: #{b[index].object_id} c: #{c[index].object_id}" 
end 
b[0] = :z 

puts "b == c => #{b == c}" 
b.each_with_index do |value, index| 
    puts "[#{index}] b: #{b[index].object_id} c: #{c[index].object_id}" 
end 

b = :a 
c = super_mega_clone(b) 
puts "b: #{b.object_id} c: #{c.object_id}" 

> clonable? false 
> clonable? true 
> c: 2153757040 
> b: 2153757480 
> b == c => true 
> [0] b: 255528 c: 255528 
> [1] b: 255688 c: 255688 
> [2] b: 374568 c: 374568 
> b == c => false 
> [0] b: 1023528 c: 255528 
> [1] b: 255688 c: 255688 
> [2] b: 374568 c: 374568 
> b: 255528 c: 255528 
1

tôi đã một git grep "can't clone" mã nguồn YARV, và có

lib/singleton.rb: raise TypeError, "can't clone instance of singleton #{self.class}" 
object.c:  rb_raise(rb_eTypeError, "can't clone %s", rb_obj_classname(obj)); 
test/test_singleton.rb: expected = "can't clone instance of singleton TestSingleton::SingletonTest" 

Các dòng đầu tiên và thứ ba cho biết bạn không thể sao chép một singleton .

Dòng thứ hai đề cập đến rb_special_const_p(obj). Nhưng điều này vượt xa ken của tôi.

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