سؤال

إذا كان لديك تجزئة (أو إشارة إلى تجزئة) في لغة Perl ذات أبعاد متعددة وتريد التكرار عبر جميع القيم، فما هي أفضل طريقة للقيام بذلك.بمعنى آخر ، إذا كان لدينا $ f-> {$ x} {$ y} ، أريد شيئًا مثل

foreach ($x, $y) (deep_keys %{$f})
{
}

بدلاً من

foreach $x (keys %f) 
    {
    foreach $y (keys %{$f->{$x}) 
    {
    }
}
هل كانت مفيدة؟

المحلول

وهنا خيارا. هذا يعمل لتجزئات عميقة تعسفا:

sub deep_keys_foreach
{
    my ($hashref, $code, $args) = @_;

    while (my ($k, $v) = each(%$hashref)) {
        my @newargs = defined($args) ? @$args : ();
        push(@newargs, $k);
        if (ref($v) eq 'HASH') {
            deep_keys_foreach($v, $code, \@newargs);
        }
        else {
            $code->(@newargs);
        }
    }
}

deep_keys_foreach($f, sub {
    my ($k1, $k2) = @_;
    print "inside deep_keys, k1=$k1, k2=$k2\n";
});

نصائح أخرى

المرحلة الأولى: لا إعادة اختراع العجلة:)

البحث على CPAN تبرزها ومفيد بشكل لا يصدق <أ href ل = "http://search.cpan.org/dist/Data-Walk/" يختلط = "نوفولو noreferrer"> بيانات :: المشي . تحديد روتين لمعالجة كل عقدة، وكنت مرتبة

use Data::Walk;

my $data = { # some complex hash/array mess };

sub process {
   print "current node $_\n";
}

walk \&process, $data;

وبوب عمك. لاحظ أنه إذا كنت تريد تمريرها تجزئة على المشي، وسوف تحتاج إلى تمرير إشارة إلى ذلك (انظر <لأ href = "http://perldoc.perl.org/perlref.html" يختلط = "نوفولو noreferrer" > perldoc perlref )، على النحو التالي (وإلا فإنه سوف محاولة لمعالجة مفاتيح التجزئة الخاص بك أيضا!):

walk \&process, \%hash;

لحل أكثر شمولا (ولكن من الصعب العثور لأول وهلة في CPAN)، استخدم <لأ href = "http://search.cpan.org/dist/Data-Visitor/lib/Data/Visitor/Callback. مساء "يختلط =" نوفولو noreferrer "> بيانات :: الزوار :: استدعاء أو وحدة الأم - هو مكتوب هذا له ميزة مما يتيح لك تحكم أكثر دقة من ما تفعله، و(فقط لمؤهلاته الشارع إضافية) باستخدام موس.

وهذا يبدو لي كما لو البيانات :: غواص أو <أ href ل = "http://search.cpan.org/perldoc؟Data::Visitor" يختلط = "نوفولو noreferrer"> بيانات :: الزوار غير النهج جيدة بالنسبة لك.

ضع في اعتبارك أن قوائم وتجزئة Perl لا تفعل ذلك يملك الأبعاد وبالتالي لا يمكن أن تكون متعددة الأبعاد.ما لك يستطيع يكون عنصر تجزئة تم تعيينه للإشارة إلى تجزئة أو قائمة أخرى.يمكن استخدام هذا لإنشاء هياكل وهمية متعددة الأبعاد.

بمجرد أن تدرك ذلك، تصبح الأمور سهلة.على سبيل المثال:

sub f($) {
  my $x = shift;
  if( ref $x eq 'HASH' ) {
    foreach( values %$x ) {
      f($_);
    }
  } elsif( ref $x eq 'ARRAY' ) {
    foreach( @$x ) {
      f($_);
    }
  }
}

أضف أي شيء آخر يجب القيام به إلى جانب اجتياز الهيكل بالطبع.

إحدى الطرق الرائعة للقيام بما تحتاجه هي تمرير مرجع التعليمات البرمجية ليتم استدعاؤه من داخل f.باستخدام النماذج الأولية الفرعية، يمكنك أيضًا جعل المكالمات تبدو مثل وظائف grep وmap الخاصة بـ Perl.

ويمكنك أيضا حلوى المصفوفات متعددة الأبعاد إذا كان لديك دائما كل القيم الأساسية، أو انك لا تحتاج للوصول إلى المستويات الفردية كما صفائف منفصلة:

$arr{"foo",1} = "one";
$arr{"bar",2} = "two";

while(($key, $value) = each(%arr))
{
    @keyValues = split($;, $key);
    print "key = [", join(",", @keyValues), "] : value = [", $value, "]\n";
}

وهذا يستخدم فاصل منخفض "$"؛ كفاصل عن قيم متعددة في المفتاح.

وليس هناك طريقة للحصول على دلالات تصفون لبالتكرار foreach على قائمة عنصر واحد في وقت واحد. سوف يكون لديك deep_keys إرجاع الضحك بصوت مرتفع (قائمة من القوائم) بدلا من ذلك. حتى أن لا يعمل في الحالة العامة للبنية بيانات التعسفي. يمكن أن يكون هناك مستويات التجزئة الفرعية متفاوتة، وبعض من مستويات يمكن أن يكون الحكام ARRAY، وما إلى ذلك.

والطريقة Perlish للقيام بذلك سيكون لكتابة الوظيفة التي يستطيع المشي على بنية بيانات التعسفي وتطبيق الاستدعاء في كل "ورقة" (أي، قيمة غير مرجعية). "الجواب هو نقطة البداية. ان وظيفة بالضبط تختلف تبعا احد ما تريد القيام به في كل مستوى. انها واضحة جدا إذا كان كل ما يهمني هو قيم ورقة. الأمور أكثر تعقيدا إذا كنت تهتم المفاتيح والمؤشرات وغيرها التي حصلت لك ورقة.

يكون الأمر سهلاً بما فيه الكفاية إذا كان كل ما تريد القيام به هو العمل على القيم، ولكن إذا كنت تريد العمل على المفاتيح، فأنت بحاجة إلى مواصفات حول كيفية استرداد المستويات.

أ.على سبيل المثال، يمكنك تحديد المفاتيح كـ "$level1_key.$level2_key.$level3_key"--أو أي فاصل يمثل المستويات.

ب.أو يمكن أن يكون لديك قائمة بالمفاتيح.

أوصي بهذا الأخير.

  • يمكن فهم المستوى من خلال @$key_stack

  • والمفتاح الأكثر محلية هو $key_stack->[-1].

  • يمكن إعادة بناء المسار من خلال: join( '.', @$key\_stack )

شفرة:

use constant EMPTY_ARRAY => [];
use strict;    
use Scalar::Util qw<reftype>;

sub deep_keys (\%) { 
    sub deeper_keys { 
        my ( $key_ref, $hash_ref ) = @_;
        return [ $key_ref, $hash_ref ] if reftype( $hash_ref ) ne 'HASH';
        my @results;

        while ( my ( $key, $value ) = each %$hash_ref ) { 
            my $k = [ @{ $key_ref || EMPTY_ARRAY }, $key ];
            push @results, deeper_keys( $k, $value );
        }
        return @results;
    }

    return deeper_keys( undef, shift );
}

foreach my $kv_pair ( deep_keys %$f ) { 
    my ( $key_stack, $value ) = @_;
    ...
}

تم اختبار هذا في بيرل 5.10.

إذا كنت تعمل مع بيانات شجرة يذهب أكثر من مستويين عميق، وتجد نفسك الرغبة في السير تلك الشجرة، يجب عليك أن تنظر أولا أنك ذاهب لجعل الكثير من العمل الإضافي لنفسك إذا كنت تخطط لreimplementing كل شيء ما عليك القيام به يدويا على تجزئات التجزئة من التجزئة عندما يكون هناك الكثير من البدائل الجيدة المتاحة (<لأ href = "http://search.cpan.org/search؟query=tree&mode=all" يختلط = "نوفولو noreferrer" > بحث CPAN ل "شجرة" ).

وعدم معرفة ما متطلبات البيانات الخاصة بك هي في الواقع، انا ذاهب الى نقطة عمياء لك في تعليمي لشجرة :: DAG_Node للحصول على انك بدأته.

وقال ذلك، Axeman هو الصحيح، وفعلت hashwalk أكثر سهولة مع العودية. وفيما يلي مثال لمساعدتك على البدء إذا كنت تشعر بأنك على الاطلاق ويجب حل مشكلتك مع تجزئات التجزئة من التجزئة:

#!/usr/bin/perl
use strict;
use warnings;

my %hash = (
    "toplevel-1" => 
    { 
        "sublevel1a"  => "value-1a",
        "sublevel1b"  => "value-1b"
    },
    "toplevel-2" =>
    {
        "sublevel1c" => 
        {
            "value-1c.1" => "replacement-1c.1",
            "value-1c.2" => "replacement-1c.2"
        },
        "sublevel1d" => "value-1d"
    }
);

hashwalk( \%hash );

sub hashwalk
{
    my ($element) = @_;
    if( ref($element) =~ /HASH/ )
    {
        foreach my $key (keys %$element)
        {
            print $key," => \n";
            hashwalk($$element{$key});
        }
    }
    else
    {
        print $element,"\n";
    }
}

ووسيتم إخراج:

toplevel-2 => 
sublevel1d => 
value-1d
sublevel1c => 
value-1c.2 => 
replacement-1c.2
value-1c.1 => 
replacement-1c.1
toplevel-1 => 
sublevel1a => 
value-1a
sublevel1b => 
value-1b

ملاحظة أنه لا يمكنك التنبؤ بما سوف يكون النظام اجتاز عناصر التجزئة إلا إذا ربط التجزئة عبر التعادل :: IxHash أو ما شابه ذلك - مرة أخرى، إذا كنت تريد الذهاب لتذهب من خلال ذلك الكثير من العمل، فإنني أوصي وحدة نمطية شجرة .

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top