Các khóa không thay đổi có ý nghĩa nói chung vì mã băm của chúng sẽ ổn định.
Đây là lý do tại sao các chuỗi được đặc biệt-chuyển đổi, trong phần này của mã MRI:
if (RHASH(hash)->ntbl->type == &identhash || rb_obj_class(key) != rb_cString) {
st_insert(RHASH(hash)->ntbl, key, val);
}
else {
st_insert2(RHASH(hash)->ntbl, key, val, copy_str_key);
}
Tóm lại, trong trường hợp chuỗi-key, st_insert2
được thông qua một con trỏ đến một chức năng mà sẽ kích hoạt dup và đóng băng.
Vì vậy, nếu chúng ta theo lý thuyết muốn hỗ trợ danh sách bất biến và băm bất biến như phím băm, sau đó chúng ta có thể sửa đổi mã đó để một cái gì đó như thế này:
VALUE key_klass;
key_klass = rb_obj_class(key);
if (key_klass == rb_cArray || key_klass == rb_cHash) {
st_insert2(RHASH(hash)->ntbl, key, val, freeze_obj);
}
else if (key_klass == rb_cString) {
st_insert2(RHASH(hash)->ntbl, key, val, copy_str_key);
}
else {
st_insert(RHASH(hash)->ntbl, key, val);
}
đâu freeze_obj
sẽ được xác định như sau:
static st_data_t
freeze_obj(st_data_t obj)
{
return (st_data_t)rb_obj_freeze((VALUE) obj);
}
Vì vậy, điều đó sẽ giải quyết sự mâu thuẫn cụ thể mà bạn quan sát được, trong đó khóa mảng có thể thay đổi. Tuy nhiên để thực sự nhất quán, nhiều loại đối tượng sẽ cần phải được thực hiện bất biến là tốt.
Không phải tất cả các loại, tuy nhiên. Ví dụ, sẽ không có điểm để đóng băng các đối tượng ngay lập tức như Fixnum vì chỉ có một thể hiện của Fixnum tương ứng với mỗi giá trị số nguyên. Đây là lý do tại sao chỉ String
cần phải được đặt theo cách đặc biệt theo cách này, không phải là Fixnum
và Symbol
.
Chuỗi là ngoại lệ đặc biệt đơn giản chỉ là vấn đề thuận tiện cho các lập trình viên Ruby, bởi vì các chuỗi thường được sử dụng làm khóa băm.
Ngược lại, lý do mà các loại đối tượng khác là không đông lạnh như thế này, mà phải thừa nhận là dẫn đến hành vi không phù hợp, chủ yếu là một vấn đề thuận tiện cho Công ty Matz & để không ủng hộ các trường hợp cạnh. Trong thực tế, tương đối ít người sẽ sử dụng một đối tượng container như một mảng hoặc một băm như một khóa băm. Vì vậy, nếu bạn làm như vậy, nó thuộc vào bạn để đóng băng trước khi chèn. Lưu ý rằng đây không phải là nghiêm chỉnh về hiệu suất, bởi vì các hành động đóng băng một đối tượng không ngay lập tức chỉ đơn giản là liên quan đến lật bit FL_FREEZE
trên bitcoin basic.flags
có mặt trên mọi đối tượng. Quay lại đầu trang | Đó là tất nhiên một hoạt động giá rẻ.
Cũng nói về hiệu suất, lưu ý rằng nếu bạn định sử dụng khóa chuỗi và bạn đang ở trong phần mã quan trọng về hiệu suất, bạn có thể muốn cố định chuỗi của mình trước khi thực hiện chèn. Nếu bạn không làm vậy, thì một số tiền được kích hoạt, đó là một hoạt động đắt tiền hơn.
Cập nhật @sawa đã chỉ ra rằng để mảng khóa của bạn đơn giản đóng băng có nghĩa là mảng ban đầu có thể bất ngờ ngoài ngữ cảnh sử dụng, điều này cũng có thể gây bất ngờ khó chịu (mặc dù otoh nó sẽ phục vụ bạn ngay để sử dụng một mảng như một khóa băm, thực sự).Do đó, nếu bạn phỏng đoán rằng việc đóng băng hai chiều là cách thoát khỏi điều đó, thì thực tế bạn sẽ phải chịu chi phí hiệu năng đáng chú ý. Trên bàn tay thứ ba, để nó không bị đóng băng hoàn toàn, và bạn nhận được sự kỳ quặc ban đầu của OP. Lạ lùng xung quanh. Một lý do khác cho Matz et al để trì hoãn các trường hợp cạnh này cho lập trình viên.
Có vẻ như, với mục đích của tôi, điều tốt nhất tôi có thể làm là 'h.keys.each {| s | h.store (s.downcase, h.delete (s))} '. – sawa
Tôi chỉ có thể đoán tại "lý do". Cũng như các chuỗi là một trường hợp sử dụng phổ biến hơn mảng, tôi nghi ngờ việc đóng băng một chuỗi sẽ dễ thực hiện hơn. Nếu tôi biết Perl, tôi sẽ xem liệu Ruby có cố gắng nhất quán với Perl trong hành vi băm của nó hay không. Nếu tôi thông thạo tiếng Nhật, tôi sẽ xem xét khi nào việc đóng băng chìa khóa được thực hiện, và xem đó có phải là kết quả của một báo cáo lỗi hay thảo luận về danh sách gửi thư (có lẽ là tiếng Nhật cho một điều gì đó sớm trong lịch sử của Ruby). –
@AndrewGrimm [Ở đây] (http://doc.ruby-lang.org/ja/1.9.2/class/Hash.html) nó nói mảng và băm không tạo khóa tốt cho băm vì chúng có thể được sửa đổi, và các chuỗi bị đóng băng để bạn không phải gọi phục hồi. Phù hợp với câu trả lời của steenslag. – sawa