มีวิธีง่าย ๆ ในการตรวจสอบความถูกต้องขององค์ประกอบแฮชที่มีอยู่และถูกกำหนดไว้หรือไม่?

ฉันต้องตรวจสอบความถูกต้องของ Perl hash ขององค์ประกอบแฮช เช่น $Table{$key1}{$key2} เพื่อให้มีอยู่และถูกกำหนดไว้ นี่คือสิ่งที่ฉันทำ (ฉันไม่รู้ว่า $key1 มีอยู่ด้วยซ้ำ)

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

มีวิธีที่ง่ายกว่าและสะอาดกว่าหรือไม่?


person WL.    schedule 27.04.2010    source แหล่งที่มา
comment
โปรดแก้ไขคำถามของคุณเพื่อใช้บล็อคโค้ดสำหรับโค้ดของคุณ   -  person justkt    schedule 27.04.2010


คำตอบ (5)


คุณไม่จำเป็นต้องตรวจสอบลำดับชั้นแต่ละระดับ เพียงเลือกมูลค่าที่คุณสนใจเท่านั้น exists จะไม่ตรวจสอบคำจำกัดความ เฉพาะในกรณีที่มีช่องในแฮชมีอยู่ (อาจมีอยู่ด้วยค่าที่ไม่ได้กำหนด) ดังนั้นหากคุณสนใจว่าค่านั้นถูกกำหนดไว้ คุณจะต้องเรียก defined แทนที่จะมีอยู่ หากไม่ได้กำหนดค่า ค่านั้นจะประเมินในบริบทบูลีนว่าเป็นเท็จ เพื่อให้เราสามารถพิมพ์น้อยลงเล็กน้อยและลดตัวอย่างของคุณเป็น:

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

อย่างไรก็ตาม หากมีการกำหนดค่าในคีย์นั้น แต่เป็น "เท็จ" (ประเมินเป็นตัวเลขเป็นศูนย์หรือเป็นสตริงว่าง) สิ่งนี้อาจทำให้เกิดผลลบลวง ดังนั้นเราควรตรวจสอบคำจำกัดความอย่างชัดเจนหากเป็นไปได้:

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

หากคุณไม่ต้องการทำให้ $Table{$key1} มีชีวิตอัตโนมัติ คุณสามารถตรวจสอบการมีอยู่ของมันก่อน ซึ่งจะนำเราไปสู่วิธีที่ "ดีที่สุด" สำหรับกรณีทั่วไป:

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

หากคุณกำลังจะทำสิ่งนี้บ่อยครั้งกับฟิลด์ต่างๆ ในแฮช คุณอาจต้องการเพิ่มวิธีการเข้าถึงแบบ OO บางอย่างซึ่งจะได้ผลสำหรับคุณ:

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

ฉันแน่ใจว่าคุณได้อ่านมันแล้ว แต่การอ่านเอกสารที่เกี่ยวข้องอีกครั้งก็ไม่เสียหายอะไร:

เมื่อกำหนดนิพจน์ที่ระบุองค์ประกอบแฮชหรือองค์ประกอบอาร์เรย์ exists จะคืนค่าเป็นจริงหากองค์ประกอบที่ระบุในแฮชหรืออาร์เรย์เคยถูกเตรียมใช้งาน แม้ว่าค่าที่เกี่ยวข้องจะไม่ได้ถูกกำหนดไว้ก็ตาม องค์ประกอบจะไม่ถูกทำให้มีชีวิตโดยอัตโนมัติหากไม่มีอยู่
...
องค์ประกอบแฮชหรืออาร์เรย์สามารถเป็นจริงได้ก็ต่อเมื่อมีการกำหนดไว้ และกำหนดไว้หากมีอยู่ แต่การย้อนกลับไม่จำเป็นต้องถือเป็นจริง

person Ether    schedule 27.04.2010
comment
สิ่งนี้จะสร้างชีวิตโดยอัตโนมัติ $Table{$key1} เสมอหากไม่มีอยู่ - person Eric Strom; 27.04.2010
comment
ฉันจะทำให้โค้ดที่ผิดโดดเด่นยิ่งขึ้นด้วยการใส่ความคิดเห็นทุกประเภทไว้รอบๆ โดยบอกว่า Don't do this!!!!!!! - person brian d foy; 27.04.2010
comment
@brian: ฉันไม่รู้ว่าฉันจะเรียกรหัสผิดรูปแบบก่อนหน้านี้หรือไม่ แค่การตรวจสอบความจริงในช่องก็เพียงพอแล้วในบางสถานการณ์ (เช่น หากการฟื้นฟูอัตโนมัติไม่สำคัญหรือไม่ใช่ปัจจัย) - person Ether; 27.04.2010
comment
การทำให้มีชีวิตอัตโนมัติโดยไม่ได้ตั้งใจเป็นสิ่งที่ผิด อย่าเพิกเฉยและสร้างนิสัยไม่ดีที่จะกัดคนในภายหลัง - person brian d foy; 27.04.2010
comment
การทำให้มีชีวิตอัตโนมัติไม่สำคัญ... จนกว่าจะเป็นเช่นนั้น และจากนั้นก็เป็น PITA ที่จะแก้ไขจุดบกพร่องของข้อมูลปลอม (ที่เสียหายทุกครั้งที่คุณวนซ้ำคีย์/ค่า) ในแฮชของคุณมาจากที่ใด - person Michael Carman; 27.04.2010

ข้อมูลต่อไปนี้สั้นกว่าและจะป้องกันจากการคืนสภาพอัตโนมัติ:

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

ไม่จำเป็นต้องตรวจสอบอื่นๆ ในรหัสของคุณ

person Eric Strom    schedule 27.04.2010

ตรวจสอบการมีอยู่ก่อน แล้วจึงกำหนดนิยาม (ค่าสามารถดำรงอยู่ได้โดยไม่ต้องกำหนด แต่ไม่สามารถกำหนดได้หากไม่มี) คุณควรทดสอบระดับกลางด้วย exists เพื่อป้องกันการทำให้มีชีวิตอัตโนมัติโดยไม่ได้ตั้งใจ สำหรับระดับสุดท้าย คุณจะต้องโทร defined เท่านั้น เมื่อมีเลเยอร์ไม่มากเกินไป การเขียนโค้ดโดยตรงก็เป็นเรื่องง่าย:

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

สิ่งนี้จะดูน่าอึดอัดใจหากมีหลายชั้น:

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

ในกรณีนั้น คุณสามารถเขียนเวอร์ชันของ defined ที่ไม่ทำให้ค่ากลางมีค่าอัตโนมัติ:

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;
}

คุณใช้มันในลักษณะนี้:

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

หมายเหตุ: ฟังก์ชันเวอร์ชันนี้มีจำนวนจำกัด

  • มันจัดการเฉพาะแฮชที่ซ้อนกันเท่านั้น Perl ช่วยให้คุณสร้างโครงสร้างข้อมูลที่กำหนดเองได้ เช่น แฮชของอาร์เรย์ของการอ้างอิงสเกลาร์...
  • ไม่รองรับการอ้างอิงที่ได้รับพร (เช่น วัตถุ)

เวอร์ชันทั่วไปอย่างแท้จริงเหลือไว้เป็นแบบฝึกหัดสำหรับผู้อ่าน ;)

person Michael Carman    schedule 27.04.2010

คุณสามารถดู Data::Diver ได้ มันดำดิ่งลงสู่โครงสร้างข้อมูลโดยไม่ต้องสร้างชีวิตอัตโนมัติ ไวยากรณ์จะเป็น:

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

หรือแม้กระทั่ง:

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

ยอดเยี่ยม! ขอบคุณทุกท่านสำหรับคำตอบครับ

เนื่องจากการทำให้มีชีวิตอัตโนมัติเป็นปัญหาสำหรับฉัน ขณะนี้ฉันกำลังใช้แนวทางที่น่าอึดอัดใจ เช่น if (มี $Table{$key1} && กำหนด $Table{$key1}{$key2}) {

ทำอะไรก็ได้

}

มันใช้งานได้สำหรับฉัน แต่อย่างที่พวกคุณพูด ฉันมีแฮชที่ซ้อนกันอยู่ลึก 3-4 ระดับ โค้ดค่อนข้างยุ่งนิดหน่อย

ฉันจะตรวจสอบ Data:Diver อันนั้นดูดีกว่า

ขอบคุณอีกครั้ง,

person WL.    schedule 28.04.2010