2010-03-22 13 views
7

एक नेस्टेड हैश संरचना %old_hash मान लें ..मैं एक नेस्टेड पर्ल हैश को एक गैर-घोंसले में कैसे साफ कर सकता हूं?

my %old_hash; 
$old_hash{"foo"}{"bar"}{"zonk"} = "hello"; 

.. जो हम "समतल करने के लिए" (खेद है कि अगर गलत शब्दावली! है) एक गैर नेस्टेड हैश को उप &flatten(...) ताकि का उपयोग कर चाहते हैं ..

sub flatten { 
    my $hashref = shift; 
    my %hash; 
    my %i = %{$hashref}; 
    foreach my $ii (keys(%i)) { 
    my %j = %{$i{$ii}}; 
    foreach my $jj (keys(%j)) { 
     my %k = %{$j{$jj}}; 
     foreach my $kk (keys(%k)) { 
     my $value = $k{$kk}; 
     $hash{$kk} = $value; 
     } 
    } 
    } 
    return %hash; 
} 

जबकि कोड छ:

my %h = &flatten(\%old_hash); 
die unless($h{"zonk"} eq "hello"); 

&flatten(...) की निम्नलिखित परिभाषा काम कर देता है iven काम यह बहुत पठनीय या साफ नहीं है।

मेरा प्रश्न है दो गुना:

  • किन तरीकों से दिए गए कोड आधुनिक पर्ल सर्वोत्तम प्रथाओं के अनुरूप नहीं है में? कठोर रहो! :-)
  • आप इसे कैसे साफ करेंगे?
+2

कृपया 'flatten' से आपका क्या मतलब है इसका एक उदाहरण दें। मुझे समझ में नहीं आता कि आपका कोड क्या करने का प्रयास कर रहा है। –

+0

साइनन: आपकी प्रतिक्रिया के लिए धन्यवाद। मैंने प्रश्न की शुरुआत में एक उपयोग उदाहरण जोड़ा है। उम्मीद है कि चीजों को स्पष्ट करें! – knorv

+0

आप उदाहरण ** ** ** में मदद करते हैं: यदि हैश है (ए => {बी => 1}, सी => {बी => 2}) 'क्या होता है? –

उत्तर

10

आपकी विधि सर्वोत्तम प्रथाओं नहीं है क्योंकि यह स्केल नहीं करती है। क्या होगा यदि नेस्टेड हैश छह है, दस स्तर गहरे हैं? पुनरावृत्ति आपको बताएगी कि एक रिकर्सिव रूटीन शायद आपको चाहिए।

sub flatten { 
    my ($in, $out) = @_; 
    for my $key (keys %$in) { 
     my $value = $in->{$key}; 
     if (defined $value && ref $value eq 'HASH') { 
      flatten($value, $out); 
     } 
     else { 
      $out->{$key} = $value; 
     } 
    } 
} 

वैकल्पिक रूप से, अच्छी आधुनिक पर्ल शैली जहां भी संभव हो सीपीएएन का उपयोग करना है। यह आम तौर पर संदर्भ द्वारा हैश पारित करने के लिए से बचने के लिए उन्हें सूचियों में बाहर का विस्तार किया और उसके बाद फिर हैश में दिया जा रहा है और अधिक कुशल है, एक अंतिम नोट के रूप में

use Data::Traverse; 
sub flatten { 
    my %hash = @_; 
    my %flattened; 
    traverse { $flattened{$a} = $b } \%hash; 
    return %flattened; 
} 

: Data::Traverse आपको क्या चाहिए करना होगा।

+1

मैं डेटा :: ट्रैवर्स का एक बड़ा प्रशंसक हूं। – friedo

+0

कोई आश्चर्य नहीं है, आप इसके मुख्य लेखक हैं;) – willert

+0

-1 कोड के आपके पहले ब्लॉक में कई टाइपो हैं, '$ value' कभी घोषित नहीं किया गया है और आपने मिश्रित हैश और हैश सिंटैक्स के संदर्भ में –

3

सबसे पहले, मैं यह सुनिश्चित करने के लिए perl -c का उपयोग करता हूं ताकि यह स्पष्ट रूप से संकलित हो सके, जो ऐसा नहीं करता है। तो, मैं इसे संकलित करने के लिए एक पीछे } जोड़ना चाहता हूं।

फिर, मैं इसे कोड लेआउट (इंडेंटेशन इत्यादि) को बेहतर बनाने के लिए perltidy के माध्यम से चलाऊंगा।

फिर, मैं perlcritic ("कठोर" मोड में) चलाता हूं ताकि स्वचालित रूप से मुझे यह बता सकें कि यह क्या सोचता है कि बुरे प्रथाएं हैं। यह शिकायत है कि:

उपनेमका "वापसी" साथ अंत नहीं है

अद्यतन: ओपी अनिवार्य रूप से कोड के बाद मैं अपने जवाब से ऊपर पोस्ट के हर लाइन बदल गया है, लेकिन मेरा मानना ​​है कि यह अभी भी लागू होता है। यह एक चलती लक्ष्य पर आसान शूटिंग नहीं है :)

1

क्या यह मूल हैश की एक प्रति या सिर्फ एक पुनर्वित्त परिणाम के साथ समाप्त करने का आपका इरादा है?

आपका कोड एक हैश (मूल हैश जो संदर्भ द्वारा उपयोग किया जाता है) से शुरू होता है और दो प्रतियां %i और %hash बनाता है।

कथन my %i=%{hashref} आवश्यक नहीं है। आप पूरे हैश को एक नए हैश में कॉपी कर रहे हैं। किसी भी मामले में (चाहे आप की प्रतिलिपि नहीं चाहते हैं) आप मूल हैश के संदर्भों का उपयोग कर सकते हैं।

यदि आपके पास हैश में हैश के पास मूल हैश के समान मूल्य है तो आप भी डेटा खो रहे हैं। क्या इसका इरादा है?

2

आपके दृष्टिकोण के साथ कुछ समस्याएं हैं जिन्हें आपको समझने की आवश्यकता है। सबसे पहले, घटना में क्या होता है कि एक ही कुंजी के साथ दो पत्ती नोड्स हैं? क्या दूसरा क्लॉबर पहला है, दूसरा अनदेखा है, क्या आउटपुट में उनकी एक सूची होनी चाहिए? यहां एक दृष्टिकोण है। पहले हम अन्य हैश गहराई से निपटने के लिए एक पुनरावर्ती समारोह का उपयोग कर कुंजी मान जोड़ों का फ्लैट सूची का निर्माण:

my %data = (
    foo => {bar => {baz => 'hello'}}, 
    fizz => {buzz => {bing => 'world'}}, 
    fad => {bad => {baz => 'clobber'}}, 
); 


sub flatten { 
    my $hash = shift; 
    map { 
     my $value = $$hash{$_}; 
     ref $value eq 'HASH' 
      ? flatten($value) 
      : ($_ => $value) 
    } keys %$hash 
} 

print join(", " => flatten \%data), "\n"; 
# baz, clobber, bing, world, baz, hello 

my %flat = flatten \%data; 

print join(", " => %flat), "\n"; 
# baz, hello, bing, world   # lost (baz => clobber) 

फिक्स कुछ इस तरह है, जो सरणी सभी मूल्यों से युक्त refs के हैश पैदा करेगा हो सकता है:

sub merge { 
    my %out; 
    while (@_) { 
     my ($key, $value) = splice @_, 0, 2; 
     push @{ $out{$key} }, $value 
    } 
    %out 
} 

my %better_flat = merge flatten \%data; 

उत्पादन कोड में, कार्यों के बीच संदर्भ पारित करना तेज़ होगा, लेकिन मैंने स्पष्टता के लिए यहां छोड़ा है।

संबंधित मुद्दे