Apakah ada cara sederhana untuk memvalidasi hash dari elemen hash yang ada dan ditentukan?

Saya perlu memvalidasi hash Perl dari elemen hash seperti $Table{$key1}{$key2} agar ada dan didefinisikan. Inilah yang saya lakukan. (Saya tidak tahu $key1 bahkan ada)

if 
((defined $Table{$key1}) &&
 (exists  $Table{$key1}) &&
 (defined $Table{$key1}{$key2}) &&
 (exists  $Table{$key1}{$key2})) 
{
   #do whatever
}

Apakah ada cara yang lebih mudah dan bersih untuk melakukannya?


person WL.    schedule 27.04.2010    source sumber
comment
Harap edit pertanyaan Anda untuk menggunakan blok kode untuk kode Anda.   -  person justkt    schedule 27.04.2010


Jawaban (5)


Anda tidak perlu memeriksa setiap tingkat hierarki: Anda cukup memilih nilai yang Anda pedulikan. exists tidak memeriksa definisi, hanya jika slot di hash ada (bisa ada dengan nilai yang tidak ditentukan), jadi jika Anda ingin nilainya ditentukan, Anda perlu memanggil defined daripada ada. Jika suatu nilai tidak ditentukan, nilai tersebut akan dievaluasi dalam konteks boolean menjadi salah, sehingga kita dapat mengetikkan lebih sedikit dan mengurangi contoh Anda menjadi:

if ($Table{$key1}{$key2})
{
   # do whatever
}

Namun, jika nilai dalam kunci tersebut ditentukan tetapi "salah" (secara numerik bernilai nol, atau berupa string kosong), hal ini dapat menyebabkan negatif palsu, jadi kita harus secara eksplisit memeriksa definisi apakah ini suatu kemungkinan:

if (defined $Table{$key1}{$key2})
{
   # do whatever
}

Jika Anda tidak ingin menghidupkan $Table{$key1} secara otomatis, Anda dapat memeriksa keberadaannya terlebih dahulu, yang membawa kita ke cara "terbaik" untuk kasus umum:

if (exists $Table{$key1} and defined $Table{$key1}{$key2})
{
   # do whatever
}

Jika Anda akan sering melakukan ini untuk berbagai bidang dalam hash, Anda mungkin ingin menambahkan beberapa metode pengakses gaya OO yang akan melakukan pekerjaan ini untuk Anda:

sub has_field
{
    my ($this, $fieldName) = @_;
    return exists $this->{data} && defined $this->{data}{$fieldName});
}

Saya yakin Anda sudah membacanya, namun tidak ada salahnya untuk membaca kembali dokumentasi yang relevan:

Diberikan ekspresi yang menentukan elemen hash atau elemen array, exists mengembalikan nilai true jika elemen yang ditentukan dalam hash atau array pernah diinisialisasi, bahkan jika nilai terkait tidak ditentukan. Elemen tidak akan dihidupkan secara otomatis jika tidak ada.
...
Elemen hash atau array bisa bernilai true hanya jika elemen tersebut didefinisikan, dan didefinisikan jika elemen tersebut ada, namun kebalikannya belum tentu benar.

person Ether    schedule 27.04.2010
comment
ini akan selalu autovivifiy $Table{$key1} jika tidak ada - person Eric Strom; 27.04.2010
comment
Saya akan membuat kode yang salah lebih menonjol dengan memberikan segala macam komentar di sekitarnya yang mengatakan Jangan lakukan ini!!!!!!! - person brian d foy; 27.04.2010
comment
@brian: Saya tidak tahu apakah saya akan menyebut formulir sebelumnya sebagai kode yang salah; cukup memeriksa kebenaran lapangan sudah cukup baik dalam beberapa keadaan (misalnya jika autovivikasi tidak menjadi masalah atau bukan merupakan faktor). - person Ether; 27.04.2010
comment
Autovivifikasi yang tidak disengaja adalah salah. Jangan abaikan dan ciptakan kebiasaan buruk yang nantinya akan menggigit orang. - person brian d foy; 27.04.2010
comment
Penghidupan otomatis tidak menjadi masalah... sampai hal itu terjadi, dan kemudian PITA untuk men-debug dari mana data palsu (yang rusak setiap kali Anda mengulang kunci/nilai) di hash Anda berasal. - person Michael Carman; 27.04.2010

Berikut ini lebih singkat dan akan melindungi dari autovivifcation:

 if (exists $table{$key1} and defined $table{$key1}{$key2}) {...}

Pemeriksaan lain dalam kode Anda tidak diperlukan.

person Eric Strom    schedule 27.04.2010

Periksa keberadaannya terlebih dahulu, kemudian keterdefinisiannya. (Suatu nilai bisa ada tanpa ditentukan tetapi tidak dapat ditentukan tanpa ada.) Anda harus menguji tingkat menengah dengan exists untuk mencegah autovivifikasi yang tidak diinginkan. Untuk level terakhir Anda hanya perlu menelepon defined. Jika lapisannya tidak terlalu banyak, mudah untuk membuat kode secara langsung:

if (exists $hash{a} && defined $hash{a}{b}) {...}

Ini menjadi janggal jika ada banyak lapisan:

if (exists $hash{a} && exists $hash{a}{b} && exists $hash{a}{b}{c} ...) {...}

Dalam hal ini, Anda dapat menulis versi defined yang tidak menghidupkan nilai perantara secara otomatis:

sub safe_defined {
    my $h = shift;

    foreach my $k (@_) {
        if (ref $h eq ref {}) {
            return unless exists $h->{$k};
            $h = $h->{$k};
        }
        else {
            return;
        }
    }

    return defined $h;
}

Anda menggunakannya dengan cara ini:

if (safe_defined(\%hash, qw(a b c))) {
     say $hash{a}{b}{c};
}

Catatan: Versi fungsi ini terbatas.

  • Ini hanya menangani hash bersarang. Perl memungkinkan Anda membuat struktur data arbitrer, seperti hash array referensi skalar...
  • Itu tidak mendukung referensi yang diberkati (yaitu objek).

Versi yang benar-benar umum dibiarkan sebagai latihan bagi pembaca. ;)

person Michael Carman    schedule 27.04.2010

Anda dapat memeriksa Data::Diver. Ini menyelami struktur data tanpa menghidupkan otomatis. Sintaksnya adalah:

if ( defined Dive(\%Table, $key1, $key2) ) { ... }

atau bahkan:

if ( defined(my $value = Dive(\%Table, $key1, $key2) ) ) {
  ...do something with $value...
}
person runrig    schedule 27.04.2010

Besar! Terima kasih atas jawabannya.

Karena penghidupan otomatis adalah masalah bagi saya, saat ini saya menggunakan pendekatan yang canggung, yaitu if (exists $Table{$key1} && mendefinisikan $Table{$key1}{$key2}) {

Lakukan apa pun

}

Ini berfungsi untuk saya, namun seperti yang kalian katakan, saya memiliki hash bersarang sedalam 3-4 level, kodenya agak berantakan.

Saya akan memeriksa Data: Penyelam. Yang itu terlihat lebih bagus.

Terima kasih lagi,

person WL.    schedule 28.04.2010