Le chiavi immutabili hanno senso in generale perché i loro codici hash saranno stabili.
Questo è il motivo per cui le stringhe sono appositamente convertiti, in questa parte del codice di 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);
}
In poche parole, nel caso di stringa-chiave, st_insert2
viene passato un puntatore a una funzione che attiverà il dup e freeze.
Quindi se in teoria ha voluto sostenere le liste immutabili e hash immutabili come chiavi di hash, allora potremmo modificare il codice a qualcosa di simile:
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);
}
Dove sarebbe definito freeze_obj
come:
static st_data_t
freeze_obj(st_data_t obj)
{
return (st_data_t)rb_obj_freeze((VALUE) obj);
}
In tal modo si risolverebbe l'incoerenza specifica osservata, in cui la chiave della matrice era mutabile. Tuttavia, per essere davvero coerenti, altri tipi di oggetti dovrebbero essere resi immutabili.
Non tutti i tipi, tuttavia. Ad esempio, non avrebbe senso congelare oggetti immediati come Fixnum perché in effetti esiste solo un'istanza di Fixnum corrispondente a ciascun valore intero. Questo è il motivo per cui solo String
deve essere dotato di custodia speciale in questo modo, non Fixnum
e Symbol
.
Le stringhe sono un'eccezione speciale per la semplicità dei programmatori Ruby, poiché le stringhe vengono spesso utilizzate come chiavi hash.
contrario, la ragione per cui altri tipi di oggetti sono non congelati simili, che porta evidentemente a comportamenti incoerenti, è soprattutto una questione di convenienza per Matz & Società non supportare casi limite. In pratica, relativamente poche persone useranno un oggetto contenitore come un array o un hash come chiave hash. Quindi, se lo fai, sta a te congelare prima dell'inserimento.
Si noti che questo non è strettamente relativo alle prestazioni, perché l'atto di congelare un oggetto non immediato implica semplicemente l'inclinazione del bit FL_FREEZE
sul campo bit basic.flags
presente su ogni oggetto. Questa è ovviamente un'operazione a basso costo.
Parlando anche di prestazioni, si noti che se si intende utilizzare chiavi di stringa e si è in una sezione di codice critico per le prestazioni, è consigliabile bloccare le stringhe prima di eseguire l'inserimento. In caso contrario, viene attivato un dup duplex, operazione più costosa.
Aggiornamento @sawa ha sottolineato che lasciare il tuo array-chiave semplicemente congelato significa la matrice originale potrebbe essere al di fuori inaspettatamente immutabili del contesto d'uso chiave, che potrebbe anche essere una brutta sorpresa (anche se OTOH servirebbe destro per usare un array come una chiave hash, davvero).Se si suppone quindi che dup + freeze sia la via d'uscita, si incorre in un costo di prestazioni evidente. Nella terza mano, lasciatelo completamente scongelato, e ottenete la stranezza dell'OP originale. Stranità tutt'intorno. Un altro motivo per Matz e altri è di rinviare questi casi limite al programmatore.
Sembra, per il mio scopo, il meglio che posso fare è 'h.keys.each {| s | h.store (s.downcase, h.delete (s))} '. – sawa
Posso solo supporre al "perché". Poiché le stringhe rappresentano un caso d'uso più comune degli array, sospetto che il congelamento di una stringa sia più semplice da implementare. Se conoscessi Perl, guarderei se Ruby sta cercando di essere coerente con Perl nel suo comportamento hash. Se fossi esperto in giapponese, guarderei quando il congelamento delle chiavi è stato implementato, e vedere se questo è il risultato di una segnalazione di bug o di una discussione su una mailing list (presumibilmente in giapponese per qualcosa così presto nella storia di Ruby). –
@AndrewGrimm [Here] (http://doc.ruby-lang.org/ja/1.9.2/class/Hash.html) dice che gli array e gli hash non rendono le chiavi valide per un hash perché possono essere modificati, e le stringhe sono bloccate in modo da non dover chiamare rehash. Coerente con la risposta di Steenslag. – sawa