पर्ल में, मैं एक हैश कैसे बनाऊंगा जिसकी कुंजी किसी दिए गए ऐरे से आती है?


80

मान लीजिए कि मेरे पास एक सरणी है, और मुझे पता है कि मैं बहुत कुछ करने जा रहा हूं "क्या सरणी में एक्स होता है?" जाँच करता है। ऐसा करने का कुशल तरीका उस सरणी को हैश में बदलना है, जहाँ कुंजियाँ सरणी के तत्व हैं, और फिर आप बस कह सकते हैं

अगर ($ हैश {X}) {...}

क्या यह सरणी-से-हैश रूपांतरण करने का एक आसान तरीका है? आदर्श रूप से, यह अनाम सरणी लेने और अनाम हैश वापस करने के लिए पर्याप्त बहुमुखी होना चाहिए।

जवाबों:


120
%hash = map { $_ => 1 } @array;

यह "@hash {@array} = ..." समाधानों जितना छोटा नहीं है, लेकिन उन लोगों को हैश और सरणी की आवश्यकता होती है जो पहले से कहीं और परिभाषित किए जा सकते हैं, जबकि यह एक अनाम सरणी ले सकता है और एक अनाम हैश लौटा सकता है।

यह क्या करता है प्रत्येक तत्व को सरणी में लेते हैं और इसे "1" के साथ जोड़ते हैं। जब यह (कुंजी, 1, कुंजी, 1, कुंजी 1) जोड़े की सूची हैश को सौंपी जाती है, तो विषम संख्या वाले हैश की कुंजी बन जाते हैं, और सम-संख्या वाले संबंधित मान बन जाते हैं।


43
 @hash{@array} = (1) x @array;

यह हैश स्लाइस, हैश से मूल्यों की एक सूची है, इसलिए यह सामने की सूची @ y @ हो जाता है।

से डॉक्स :

यदि आप इस बारे में भ्रमित हैं कि आप '%' के बजाय हैश स्लाइस पर '@' का उपयोग क्यों करते हैं, तो इसे इस तरह से सोचें। ब्रैकेट (चौकोर या घुंघराले) का प्रकार नियंत्रित करता है कि यह एक सरणी है या एक हैश देखा जा रहा है। दूसरी ओर, सरणी या हैश पर अग्रणी प्रतीक ('$' या '@') इंगित करता है कि क्या आप एक विलक्षण मान (एक स्केलर) या एक बहुवचन (एक सूची) वापस प्राप्त कर रहे हैं।


1
वाह, मैंने कभी नहीं सुना (या सोचा) कि एक। धन्यवाद! मुझे यह समझने में परेशानी हो रही है कि यह कैसे काम करता है। क्या आप एक स्पष्टीकरण जोड़ सकते हैं? विशेष रूप से, आप% हैश नामक हैश कैसे ले सकते हैं और इसे @ चिह्न के साथ संदर्भित कर सकते हैं?
राल्दी

2
raldi: यह एक हैश स्लाइस, हैश से मूल्यों की एक सूची है, इसलिए यह सूची-y @ सामने मिलती है। Perldoc.perl.org/perldata.html#Slices देखें - विशेष रूप से अनुभाग का अंतिम पैराग्राफ
ysth

आपको उसे अपने उत्तर में जोड़ना चाहिए!
राल्दी

क्या आप आरएचएस को भी समझा सकते हैं? धन्यवाद।
सुशील जावड़ी

1
(सूची) x $ संख्या सूची को $ संख्या बार दोहराती है। स्केलर संदर्भ में एक सरणी का उपयोग करने से तत्वों की संख्या वापस आ जाती है, इसलिए (1) x @array 1s की एक सूची है जिसकी लंबाई @array जैसी है।
मोरिट्ज़

39
@hash{@keys} = undef;

यहाँ सिंटैक्स जहाँ आप हैश के साथ @एक हैश स्लाइस का उल्लेख कर रहे हैं । हम मूल रूप से कह रहे हैं $hash{$keys[0]}और $hash{$keys[1]}और $hash{$keys[2]}... =, एक lvalue के बाएं हाथ की ओर एक सूची है, और हम उस सूची, जो वास्तव में हैश और सेट सभी नामित चाबी के लिए मूल्यों में चला जाता है के लिए बताए जा रहे हैं। इस स्थिति में, मैंने केवल एक मान निर्दिष्ट किया है, जिससे कि मान में चला जाता है $hash{$keys[0]}, और दूसरे हैश अपरिभाषित मूल्यों के साथ सभी ऑटो-विविफाई (जीवन में आते हैं) प्रविष्ट करते हैं। [मेरा मूल सुझाव यहाँ अभिव्यक्ति = 1 निर्धारित किया गया था, जो कि 1 के लिए एक कुंजी और दूसरों को निर्धारित करेगा undef। मैंने इसे स्थिरता के लिए बदल दिया है, लेकिन जैसा कि हम नीचे देखेंगे, सटीक मान मायने नहीं रखते हैं।]

जब आपको पता चलता है कि लैवल्यू, बाएं हाथ की ओर =, की अभिव्यक्ति हैश से निर्मित एक सूची है, तो यह कुछ समझ में आने लगेगा कि हम इसका उपयोग क्यों कर रहे हैं @। [के अलावा मुझे लगता है कि यह पर्ल 6 में बदल जाएगा]

यहाँ विचार यह है कि आप हैश का उपयोग एक सेट के रूप में कर रहे हैं। क्या मायने रखता है मैं जो मान नहीं दे रहा हूं; यह सिर्फ कुंजियों का अस्तित्व है। तो आप जो करना चाहते हैं वह कुछ ऐसा नहीं है:

if ($hash{$key} == 1) # then key is in the hash

बजाय:

if (exists $hash{$key}) # then key is in the set

यह वास्तव existsमें हैश में मूल्य के साथ परेशान करने के बजाय चेक चलाने के लिए अधिक कुशल है , हालांकि मेरे लिए यहां महत्वपूर्ण बात यह है कि आप केवल हैश की कुंजी के साथ एक सेट का प्रतिनिधित्व कर रहे हैं। इसके अलावा, किसी ने बताया कि undefयहां मूल्य के रूप में उपयोग करके , हम एक मूल्य निर्दिष्ट करने की तुलना में कम संग्रहण स्थान का उपभोग करेंगे। (और कम भ्रम भी उत्पन्न करते हैं, क्योंकि मान मायने नहीं रखता है, और मेरा समाधान केवल हैश में पहले तत्व के लिए एक मूल्य प्रदान करेगा और दूसरों को छोड़ देगा undef, और कुछ अन्य समाधान कार्टवेहील्स को मोड़ने के लिए मूल्यों की एक सरणी बनाने के लिए बदल रहे हैं। हैश; पूरी तरह से व्यर्थ प्रयास)।


1
यह एक दूसरे पर बेहतर है क्योंकि यह हैश को इनिशियलाइज़ करने के लिए एक अस्थायी सूची नहीं बनाता है। यह तेज होना चाहिए और कम मेमोरी का उपभोग करना चाहिए।
लियोन टिम्मरमैन

1
फ्रॉस्टी: आपको "मेरा% हैश" पहले घोषित करना होगा, फिर "@ हश {@arr} = 1" (कोई "मेरा") नहीं करना होगा।
माइकल कार्मन सेप

8
= (), नहीं = undef, सिर्फ पहले के बाद ही नहीं सभी मूल्यों के लिए अनिर्दिष्ट रूप से उपयोग करने की निरंतरता के लिए। (जैसा कि इन टिप्पणियों में दिखाया गया है, यह देखना बहुत आसान है undefऔर लगता है कि इसे सिर्फ 1 में बदला जा सकता है और सभी हैश मूल्यों को प्रभावित कर सकता है।)
ysth

2
जैसे-जैसे मान यहां "अपरिभाषित" के रूप में समाप्त होते हैं (और शायद आपके लिए बहुत कारण नहीं है - जैसा कि ysth ने बताया है) आप कोड को हैश का उपयोग नहीं कर सकते हैं जैसे "if ($ hash {$ value})"। आपको "if ($ हैश {$ value} मौजूद है)" की आवश्यकता होगी।
डेव क्रॉस

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

16

ध्यान दें कि यदि टाइपिंग if ( exists $hash{ key } )आपके लिए बहुत अधिक काम नहीं है (जो कि मैं उपयोग करना पसंद करता हूं क्योंकि ब्याज की बात वास्तव में इसके मूल्य की सत्यता के बजाय एक कुंजी की उपस्थिति है), तो आप छोटी और प्यारी का उपयोग कर सकते हैं

@hash{@key} = ();

8

मैंने हमेशा यही सोचा था

foreach my $item (@array) { $hash{$item} = 1 }

कम से कम अच्छा और पठनीय / बनाए रखने योग्य था।


7

यहाँ एक अनुमान है, कि "क्या सरणी में X होता है?" चेक को सरणी को हैश में बदलना है। दक्षता दुर्लभ संसाधन पर निर्भर करती है, अक्सर समय लेकिन कभी-कभी अंतरिक्ष और कभी-कभी प्रोग्रामर प्रयास। आप कम से कम एक साथ एक सूची और सूची का हैश रखकर खपत की गई मेमोरी को दोगुना कर रहे हैं। साथ ही आप अधिक मूल कोड लिख रहे हैं जिसे आपको परीक्षण, दस्तावेज़, आदि की आवश्यकता होगी

एक वैकल्पिक, देखो सूची :: MoreUtils मॉड्यूल में, विशेष रूप से कार्य करता है के रूप में any(), none(), true()और false()। वे सभी सशर्त और तर्क के रूप में एक सूची के रूप में एक ब्लॉक लेते हैं, map()और grep():

print "At least one value undefined" if any { !defined($_) } @list;

मैंने एक त्वरित परीक्षा चलाई, जो एक सरणी (25000 शब्दों) में आधे / usr / शेयर / तानाशाह / शब्दों के आधे हिस्से में लोड हो रही थी, फिर सरणी में पूरे शब्दकोश (हर 5000 वें शब्द) से चुने गए ग्यारह शब्दों की तलाश में, दोनों सरणी का उपयोग करके any()सूची से विधि और कार्य :: MoreUtils।

स्रोत से निर्मित पर्ल 5.8.8 पर, अरै-टू-हैश विधि विधि की तुलना में लगभग any()1100 गुना तेज है (उबंटू 6.06 के पैक किए गए पर्ल 5.8.7 के तहत तेजी से 1300x)।

हालांकि यह पूरी कहानी नहीं है - सरणी-से-हैश रूपांतरण में लगभग 0.04 सेकंड लगते हैं जो इस मामले में सरणी-से-हैश विधि के समय की दक्षता को 1.5x-2x any()विधि से अधिक तेजी से मारता है । अभी भी अच्छा है, लेकिन लगभग नहीं के रूप में तारकीय।

मेरी आंत की भावना यह है कि सरणी-से-हैश विधि any()ज्यादातर मामलों में हरा देने वाली है, लेकिन अगर मैं कुछ और ठोस मैट्रिक्स (बहुत सारे परीक्षण मामले, सभ्य सांख्यिकीय विश्लेषण, शायद कुछ बड़ा हो तो मुझे बहुत अच्छा लगेगा) हे प्रत्येक विधि, आदि का एल्गोरिथम विश्लेषण) आपकी आवश्यकताओं के आधार पर, सूची :: MoreUtils एक बेहतर समाधान हो सकता है; यह निश्चित रूप से अधिक लचीला है और कम कोडिंग की आवश्यकता है। याद रखें, समयपूर्व अनुकूलन एक पाप है ... :)


इस सवाल का जवाब नहीं है। यह भी याद आती है ... सरणी हैश रूपांतरण केवल एक बार होता है ... कुल 0.04 सेकंड (2008 में) कार्यक्रम के रन समय में जोड़ा गया, जबकि लुकअप कई बार होते हैं।
जिम बेल्टर

2
मैंने अंतर्निहित समस्या को हल करने का प्रयास किया न कि केवल प्रश्न का उत्तर दें। List::MoreUtilsउपयोग के मामले के आधार पर एक उपयुक्त विधि हो सकती है या नहीं भी हो सकती है। आपके उपयोग के मामले में कई लुकअप हो सकते हैं; दूसरों को नहीं हो सकता है। मुद्दा यह है कि दोनों सरणी-से-हैश रूपांतरण और सदस्यता का निर्धारण List::MoreUtilsकरने की अंतर्निहित समस्या को हल करते हैं ; कई दृष्टिकोणों को जानने से आप अपने विशिष्ट उपयोग के मामले में सर्वोत्तम विधि चुन सकते हैं।
ArcLight

5

पर्ल 5.10 में, क्लोज-टू-मैजिक ~~ ऑपरेटर है:

sub invite_in {
    my $vampires = [ qw(Angel Darla Spike Drusilla) ];
    return ($_[0] ~~ $vampires) ? 0 : 1 ;
}

यहां देखें: http://dev.perl.org/perl5/news/2007/perl-5.10.0.html


1
यदि यह एक बड़े सरणी के लिए कई बार कर रहा है, तो संभवतः यह बहुत धीमा होने वाला है।
ysth

1
यह "स्मार्ट मैच ऑपरेटर" :)
brian d foy

5

इसके अलावा पूर्णता के लिए ध्यान देने योग्य है, 2 समान-लंबाई सरणियों के साथ यह करने के लिए मेरी सामान्य विधि @keysऔर @valsजिसे आप पसंद करेंगे वह हैश ...

my %hash = map { $keys[$_] => $vals[$_] } (0..@keys-1);


4
के लिए सामान्य मुहावरा @keys-1है $#keys
स्टीफन माज्यूस्की

@StefanMajewsky मैंने नहीं देखा कि वास्तव में थोड़ी देर में एक का उपयोग किया गया था। मैं खुद इससे दूर रहता हूं - यह बदसूरत है।
तमजिन ब्लेक

3

रल्डी के घोल को कस दिया जा सकता है (मूल से '=>' आवश्यक नहीं है):

my %hash = map { $_,1 } @array;

इस तकनीक का उपयोग टेक्स्ट सूची को हैश में बदलने के लिए भी किया जा सकता है:

my %hash = map { $_,1 } split(",",$line)

इसके अतिरिक्त यदि आपके पास इस तरह के मान हैं: "foo = 1, bar = 2, baz = 3" आप यह कर सकते हैं:

my %hash = map { split("=",$_) } split(",",$line);

[शामिल करने के लिए संपादित करें]


एक और समाधान पेश किया गया (जिसमें दो लाइनें हैं):

my %hash;
#The values in %hash can only be accessed by doing exists($hash{$key})
#The assignment only works with '= undef;' and will not work properly with '= 1;'
#if you do '= 1;' only the hash key of $array[0] will be set to 1;
@hash{@array} = undef;

1
$ _ => 1 और $ _, 1 के बीच का अंतर विशुद्ध रूप से शैलीगत है। व्यक्तिगत रूप से मैं => पसंद करता हूं क्योंकि यह कुंजी / मूल्य लिंक को अधिक स्पष्ट रूप से इंगित करता है। आपका @hash {@array} = 1 समाधान काम नहीं करता है। मूल्यों में से केवल एक (@array में पहली कुंजी के साथ जुड़ा हुआ) 1 पर सेट हो जाता है
डेव क्रॉस

2

आप Perl6 :: Junction का उपयोग भी कर सकते हैं ।

use Perl6::Junction qw'any';

my @arr = ( 1, 2, 3 );

if( any(@arr) == 1 ){ ... }

1
यदि यह एक बड़े सरणी के लिए कई बार कर रहा है, तो संभवतः यह बहुत धीमा होने वाला है।
ysth

1
वास्तव में इसे एक बार करना बहुत धीमा है। इसे एक ऑब्जेक्ट बनाना होगा। फिर कुछ ही समय बाद, यह उस वस्तु को नष्ट कर देगा। यह केवल एक उदाहरण है कि क्या संभव है।
ब्रैड गिल्बर्ट

1

यदि आप बहुत सारे सेट थ्योरिटिक ऑपरेशन करते हैं - तो आप सेट :: स्केलर या इसी तरह के मॉड्यूल का भी उपयोग कर सकते हैं । फिर $s = Set::Scalar->new( @array )आपके लिए सेट का निर्माण करेगा - और आप इसके साथ क्वेरी कर सकते हैं $s->contains($m):।


1

यदि आप अपने नाम स्थान को प्रदूषित नहीं करना चाहते हैं, तो आप कोड को एक सबरूटीन में रख सकते हैं।

my $hash_ref =
  sub{
    my %hash;
    @hash{ @{[ qw'one two three' ]} } = undef;
    return \%hash;
  }->();

या इससे भी बेहतर:

sub keylist(@){
  my %hash;
  @hash{@_} = undef;
  return \%hash;
}

my $hash_ref = keylist qw'one two three';

# or

my @key_list = qw'one two three';
my $hash_ref = keylist @key_list;

यदि आप वास्तव में एक सरणी संदर्भ पास करना चाहते हैं:

sub keylist(\@){
  my %hash;
  @hash{ @{$_[0]} } = undef if @_;
  return \%hash;
}

my @key_list = qw'one two three';
my $hash_ref = keylist @key_list;

%hash = map{ $_, undef } @keylist
ब्रैड गिल्बर्ट

1
#!/usr/bin/perl -w

use strict;
use Data::Dumper;

my @a = qw(5 8 2 5 4 8 9);
my @b = qw(7 6 5 4 3 2 1);
my $h = {};

@{$h}{@a} = @b;

print Dumper($h);

देता है (नोट दोहराया चाबियाँ सरणी में सबसे बड़ी स्थिति में मूल्य मिलता है - यानी 8-> 2 और 6 नहीं)

$VAR1 = {
          '8' => '2',
          '4' => '3',
          '9' => '1',
          '2' => '5',
          '5' => '4'
        };

एक हैडर यहाँ एक छोटे से अधिक से अधिक लगता है।
बोब्बोगो

0

तुम भी टाई :: IxHash , जो लागू करने के लिए साहचर्य सरणियों का आदेश दिया जाँच करना चाहते हो सकता है । यह आपको अपने डेटा की एक प्रति पर दोनों प्रकार के लुकअप (हैश और इंडेक्स) करने की अनुमति देगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.