अगर एक पर्ल सरणी में कोई विशेष मान है तो मैं कैसे जांच सकता हूं?


239

मैं सरणी के माध्यम से पुनरावृत्ति किए बिना किसी सरणी में मान के अस्तित्व की जाँच करने का एक तरीका जानने की कोशिश कर रहा हूं।

मैं एक पैरामीटर के लिए एक फाइल पढ़ रहा हूं। मेरे पास उन मापदंडों की एक लंबी सूची है जिनसे मैं निपटना नहीं चाहता। मैंने इन अवांछित मापदंडों को एक सरणी में रखा @badparams

मैं एक नया पैरामीटर पढ़ना चाहता हूं और यदि यह मौजूद नहीं है @badparams, तो इसे प्रोसेस करें। यदि यह मौजूद है @badparams, तो अगले रीड पर जाएं।


3
रिकॉर्ड के लिए, उत्तर आपकी स्थिति पर निर्भर करता है। ऐसा लगता है कि आप बार-बार लुकअप करना चाहते हैं, इसलिए हैकर का उपयोग करना जैसा कि jkramer से पता चलता है कि यह अच्छा है। यदि आप केवल एक खोज करना चाहते हैं, तो आप केवल पुनरावृति कर सकते हैं। (और कुछ मामलों में आप हैश का उपयोग करने के बजाय द्विआधारी खोज करना चाह सकते हैं!)
कैस्केबेल


6
रिकॉर्ड के लिए (और यह आपकी स्थिति के लिए पूरी तरह से अनुपयुक्त हो सकता है) आम तौर पर 'अच्छे मूल्यों' की पहचान करना और बाकी 'बुरे मूल्यों' को भुनाने की बजाय बाकी चीजों को नजरअंदाज करना बेहतर उपाय है। आपके द्वारा पूछे जाने वाले प्रश्न यह है कि क्या यह संभव है कि कुछ बुरे मूल्य हो सकते हैं जिन्हें आप अभी तक नहीं जानते हैं।
मैकलीन को अनुदान दें

जवाबों:


187

बस सरणी को हैश में बदलें:

my %params = map { $_ => 1 } @badparams;

if(exists($params{$someparam})) { ... }

आप सूची में अधिक (अद्वितीय) परम भी जोड़ सकते हैं:

$params{$newparam} = 1;

और बाद में (अद्वितीय) वापस की सूची प्राप्त करें:

@badparams = keys %params;

38
रिकॉर्ड के लिए, यह कोड अभी भी सरणी के माध्यम से पुनरावृति करता है। नक्शा {} कॉल बस उस पुनरावृत्ति को टाइप करने के लिए बहुत आसान बनाता है।
केनी वायलैंड

3
मैं यह केवल तभी करूंगा जब @badparams में आपके मान छद्म-स्थिर हों और आप मानचित्र के विरुद्ध बहुत सारे चेक करने की योजना बनाते हैं। मैं एकल जाँच के लिए इसकी अनुशंसा नहीं करूँगा।
आरोन टी हैरिस

क्या यह एक ही मूल्य के साथ कई मदों के साथ एक सरणी के लिए धमाका नहीं करेगा?
रॉब वेल्स

3
@RobWells नहीं, यह ठीक काम करेगा। अगली बार यह उसी मूल्य को देखता है, यह केवल हैश में प्रविष्टि को अधिलेखित करेगा, जो इस मामले में इसे 1फिर से सेट करता है ।
andrrjones 10

222

सर्वश्रेष्ठ सामान्य उद्देश्य - विशेष रूप से लघु सरणियों (1000 आइटम या उससे कम) और कोडर जो कि अनुकूलन के अनिश्चित हैं, उनकी आवश्यकताओं के अनुरूप है।

# $value can be any regex. be safe
if ( grep( /^$value$/, @array ) ) {
  print "found it";
}

यह उल्लेख किया गया है कि grep सभी मानों से होकर गुजरता है, भले ही सरणी में पहला मान मेल खाता हो। यह सच है, हालांकि ज्यादातर मामलों के लिए grep अभी भी बहुत तेज है । यदि आप छोटे सरणियों (1000 से कम आइटम) के बारे में बात कर रहे हैं, तो अधिकांश एल्गोरिदम वैसे भी बहुत तेज़ होने वाले हैं। यदि आप बहुत लंबी सरणियों (1,000,000 आइटम) के बारे में बात कर रहे हैं, तो इस बात की परवाह किए बिना कि क्या आइटम पहले या बीच में है या सरणी में अंतिम है, क्रेप काफी जल्दी है।

अब सरणियों के लिए अनुकूलन के मामले:

यदि आपकी सरणी सॉर्ट की गई है , तो "बाइनरी खोज" का उपयोग करें।

यदि एक ही सरणी को बार-बार कई बार खोजा जाता है, तो इसे पहले एक हैश में कॉपी करें और फिर हैश की जांच करें। यदि मेमोरी एक चिंता का विषय है, तो सरणी से प्रत्येक आइटम को हैश में स्थानांतरित करें। अधिक स्मृति कुशल लेकिन मूल सरणी को नष्ट कर देता है।

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

नोट: ये अनुकूलन केवल तभी होगा जब लंबी सरणियों के साथ काम किया जाए। अनुकूलन खत्म मत करो।


12
डबल टिल्ड को पर्ल 5.10 में पेश किया गया था
अगली सूचना तक रोक दिया गया।

15
@ डेनिसविलियम्सन ... और 5.18 में इसे प्रायोगिक माना जाता है
Xaerxess

5
उत्पादन कोड में स्मार्टमैच से बचें। यह अस्थिर / प्रयोगात्मक लंबित है और आगे की सूचना है।
वेक्टर गोरगोथ

1
मुझे यह और भी पठनीय लगता है, लेकिन यह मत कहो कि यह कुशल नहीं है और हर तत्व की जांच करता है, भले ही यह पहला हो।
जिओरडनो

7
अगर ("मान" ~~ @ सार) का उपयोग न करें। ~~ एक प्रयोगात्मक विशेषता है जिसे स्मार्टमैच कहा जाता है। प्रयोग विफलता के रूप में समझा जाता है और पर्ल के भविष्य के संस्करण में हटा दिया जाएगा या संशोधित किया जाएगा।
यरहमान

120

आप स्मार्टमंच सुविधा का उपयोग पर्ल 5.10 में निम्नानुसार कर सकते हैं:

नीचे दिए गए शाब्दिक मूल्य लुकअप के लिए ट्रिक करना होगा।

if ( "value" ~~ @array ) 

स्केलर लुकअप के लिए, नीचे दिया गया कार्य ऊपर की तरह काम करेगा।

if ($val ~~ @array)

नीचे इनलाइन एरे के लिए, ऊपर की तरह काम करेगा।

if ( $var ~~ ['bar', 'value', 'foo'] ) 

में पर्ल 5.18 smartmatch प्रयोगात्मक रूप में चिह्नित किया गया है इसलिए आप को चालू करके चेतावनी को बंद करना होगा प्रयोगात्मक अपनी स्क्रिप्ट / मॉड्यूल के लिए नीचे जोड़कर pragma:

use experimental 'smartmatch';

वैकल्पिक रूप से यदि आप स्मार्टमैच के उपयोग से बचना चाहते हैं - तो जैसा कि हारून ने कहा है कि उपयोग करें:

if ( grep( /^$value$/, @array ) ) {
  #TODO:
}

4
यह अच्छा है, लेकिन पर्ल 5.10 के लिए नया लगता है। कुछ समय पहले मैंने सोचा कि मुझे वाक्यविन्यास त्रुटियां क्यों हो रही हैं।
इगोर स्कोकिंस्की

17
चेतावनी: आप इससे बचना चाहते हैं, क्योंकि ऑपरेटर के पास अलग-अलग संस्करणों में स्पष्ट रूप से अलग व्यवहार होता है, और इस बीच इसे प्रयोगात्मक के रूप में चिह्नित किया गया है । इसलिए जब तक आपको अपने पर्ल संस्करण पर पूर्ण नियंत्रण नहीं होता (और जिसके पास है) आपको शायद इससे बचना चाहिए।
भूलभुलैया

1
मुझे यह स्पष्टीकरण पसंद है कि सेटिंग use experimental 'smartmatch'की सिफारिश क्यों की जाती है। जैसा कि मेरे पर्ल संस्करण (आंतरिक प्रणाली) का नियंत्रण है, मैं no warnings 'experimental::smartmatch';कथन का उपयोग करता हूं ।
लेप

43

यह ब्लॉग पोस्ट इस प्रश्न के सर्वोत्तम उत्तरों पर चर्चा करता है।

एक संक्षिप्त सारांश के रूप में, यदि आप CPAN मॉड्यूल स्थापित कर सकते हैं तो सबसे पठनीय समाधान हैं:

any(@ingredients) eq 'flour';

या

@ingredients->contains('flour');

हालाँकि, एक अधिक सामान्य मुहावरा है:

any { $_ eq 'flour' } @ingredients

लेकिन कृपया first()फ़ंक्शन का उपयोग न करें ! यह आपके कोड के इरादे को बिल्कुल भी व्यक्त नहीं करता है। ~~"स्मार्ट मैच" ऑपरेटर का उपयोग न करें : यह टूट गया है। और न grep()ही हैश के साथ समाधान का उपयोग करें : वे पूरी सूची के माध्यम से पुनरावृति करते हैं।

any() जैसे ही यह अपना मूल्य पाता है, रुक जाएगा।

अधिक जानकारी के लिए ब्लॉग पोस्ट देखें।


8
किसी भी जरूरत हैuse List::Util qw(any);List::Utilमें है कोर मॉड्यूल
ओथजॉब

13

विधि 1: grep (सावधान हो सकता है जबकि मूल्य regex होने की उम्मीद है)।

grepसंसाधनों को देखते हुए , उपयोग से बचने की कोशिश करें ।

if ( grep( /^$value$/, @badparams ) ) {
  print "found";
}

विधि 2: रैखिक खोज

for (@badparams) {
    if ($_ eq $value) {
       print "found";
       last;
    }
}

विधि 3: एक हैश का उपयोग करें

my %hash = map {$_ => 1} @badparams;
print "found" if (exists $hash{$value});

विधि 4: स्मार्टमैच

(पर्ल 5.10 में जोड़ा गया है, पर्ल 5.18 में प्रयोगात्मक है)।

use experimental 'smartmatch';  # for perl 5.18
print "found" if ($value ~~ @badparams);

विधि 5: मॉड्यूल का उपयोग करें List::MoreUtils

use List::MoreUtils qw(any);
@badparams = (1,2,3);
$value = 1;
print "found" if any {$_ == $value} @badparams;

12

@ eakssjo का बेंचमार्क टूट गया है - लूप में हैश बनाने के उपाय बनाम लूप में रेगीक्स बनाने। निश्चित संस्करण (प्लस मैंने जोड़ा है List::Util::firstऔर List::MoreUtils::any):

use List::Util qw(first);
use List::MoreUtils qw(any);
use Benchmark;

my @list = ( 1..10_000 );
my $hit = 5_000;
my $hit_regex = qr/^$hit$/; # precompute regex
my %params;
$params{$_} = 1 for @list;  # precompute hash
timethese(
    100_000, {
        'any' => sub {
            die unless ( any { $hit_regex } @list );
        },
        'first' => sub {
            die unless ( first { $hit_regex } @list );
        },
        'grep' => sub {
            die unless ( grep { $hit_regex } @list );
        },
        'hash' => sub {
            die unless ( $params{$hit} );
        },
    });

और परिणाम (यह 100_000 पुनरावृत्तियों के लिए, @'ssjo के उत्तर से दस गुना अधिक है):

Benchmark: timing 100000 iterations of any, first, grep, hash...
       any:  0 wallclock secs ( 0.67 usr +  0.00 sys =  0.67 CPU) @ 149253.73/s (n=100000)
     first:  1 wallclock secs ( 0.63 usr +  0.01 sys =  0.64 CPU) @ 156250.00/s (n=100000)
      grep: 42 wallclock secs (41.95 usr +  0.08 sys = 42.03 CPU) @ 2379.25/s (n=100000)
      hash:  0 wallclock secs ( 0.01 usr +  0.00 sys =  0.01 CPU) @ 10000000.00/s (n=100000)
            (warning: too few iterations for a reliable count)

6
यदि आप कई तत्वों का परीक्षण करना चाहते हैं, तो अग्रिम में हैश बनाने से आपका समय बचता है। लेकिन अगर आप यह जानना चाहते हैं कि क्या इसमें एक भी तत्व शामिल है, तो आपके पास पहले से हैश नहीं है। इसलिए, हैश बनाना कंप्यूटिंग समय का हिस्सा होना चाहिए। नियमित अभिव्यक्ति के लिए और भी अधिक: आपको प्रत्येक तत्व की तलाश में एक नया regexp चाहिए।
मछली पालन

1
@fishinear True, लेकिन यदि आप केवल एक चेक में रुचि रखते हैं, एक से अधिक चेक के लिए नहीं, तो जाहिर है कि यह microoptimization भी है कि कौन सी विधि जल्दी है क्योंकि उन microseconds कोई फर्क नहीं पड़ता। यदि आप इस चेक को फिर से करना चाहते हैं, तो हैश जाने का तरीका है, हैश बनाने की लागत एक बार काफी कम है तो इसे नजरअंदाज किया जा सकता है। ऊपर बेंचमार्क केवल परीक्षण के विभिन्न तरीकों को मापता है , किसी भी सेटअप को शामिल नहीं करता है। हां, यह आपके उपयोग के मामले में अमान्य हो सकता है, लेकिन फिर से - यदि आप केवल एक ही जाँच कर रहे हैं तो आपको अपने और अपने साथियों के लिए सबसे अधिक पठनीय उपयोग करना चाहिए।
Xaerxess

10

भले ही यह उपयोग करने के लिए सुविधाजनक है, लेकिन ऐसा लगता है कि कन्वर्ट-टू-हैश समाधान की कीमत काफी प्रदर्शन है, जो मेरे लिए एक मुद्दा था।

#!/usr/bin/perl
use Benchmark;
my @list;
for (1..10_000) {
    push @list, $_;
}

timethese(10000, {
  'grep'    => sub {
            if ( grep(/^5000$/o, @list) ) {
                # code
            }
        },
  'hash'    => sub {
            my %params = map { $_ => 1 } @list;
            if ( exists($params{5000}) ) {
                # code
            }
        },
});

बेंचमार्क टेस्ट का आउटपुट:

Benchmark: timing 10000 iterations of grep, hash...
          grep:  8 wallclock secs ( 7.95 usr +  0.00 sys =  7.95 CPU) @ 1257.86/s (n=10000)
          hash: 50 wallclock secs (49.68 usr +  0.01 sys = 49.69 CPU) @ 201.25/s (n=10000)

5
List::Util::firstजब इसका मिलान होता है तो इसका उपयोग तेज होता है।
रॉबरेल

1
-1 आपके बेंचमार्क में दोष हैं, हैश बनाने और लुकअप करने की तुलना grepमें काफी धीमा है, क्योंकि पूर्व O (n) और बाद वाला O (1) है। बस हैश निर्माण केवल एक बार (लूप के बाहर) करें और केवल तरीकों को मापने के लिए regex को दबाएं ( मेरा उत्तर देखें )।
Xaerxess

4
@Xaerxess: मेरे मामले में मैं एक लुकअप करना चाहता था , इसलिए मुझे लगता है कि हैश / रेगेक्स क्रिएशन और लुकअप / grep दोनों को गिनना उचित है। यह कार्य कई लुकअप करना होगा, मुझे लगता है कि आपका समाधान बेहतर है।
6

3
यदि आप केवल एक पुनरावृत्ति करना चाहते हैं, तो अंतर आपके द्वारा चुने गए किसी भी तरीके के बीच अविभाज्य है, इसलिए कोई भी बेंचमार्क गलत है क्योंकि यह इस मामले में एक बुराई माइक्रोप्टीमाइजेशन है।
Xaerxess

2
रेगेक्स को केवल एक बार संकलित किया गया है, क्योंकि इसमें ध्वज 'ओ' है।
एपोक

3

@files एक मौजूदा सरणी है

my @new_values =  grep(/^2[\d].[\d][A-za-z]?/,@files);

print join("\n", @new_values);

print "\n";

/ ^ 2 ई-रीडिंग।


2

आप निश्चित रूप से यहां एक हैश चाहते हैं। हैश में खराब पैरामीटर रखें, फिर तय करें कि हैश में कोई विशेष पैरामीटर मौजूद है या नहीं।

our %bad_params = map { $_ => 1 } qw(badparam1 badparam2 badparam3)

if ($bad_params{$new_param}) {
  print "That is a bad parameter\n";
}

यदि आप वास्तव में इसे एक सरणी के साथ करने में रुचि रखते हैं, तो देखें List::UtilयाList::MoreUtils


0

ऐसा करने के दो तरीके हैं। आप अन्य पोस्ट द्वारा सुझाए गए मानों को लुकअप तालिका के लिए हैश में फेंक सकते हैं। (मैं सिर्फ एक और मुहावरा जोड़ूंगा।)

my %bad_param_lookup;
@bad_param_lookup{ @bad_params } = ( 1 ) x @bad_params;

लेकिन अगर यह ज्यादातर शब्द वर्णों का डेटा है और बहुत अधिक मेटा नहीं है, तो आप इसे रेगेक्स अल्टरनेशन में डंप कर सकते हैं:

use English qw<$LIST_SEPARATOR>;

my $regex_str = do { 
    local $LIST_SEPARATOR = '|';
    "(?:@bad_params)";
 };

 # $front_delim and $back_delim being any characters that come before and after. 
 my $regex = qr/$front_delim$regex_str$back_delim/;

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


1
आप भी लिख सकते हैं @bad_param_lookup{@bad_params} = (), लेकिन आपको existsसदस्यता की जांच करने के लिए उपयोग करना होगा।
ग्रेग बेकन

-1
my @badparams = (1,2,5,7,'a','zzz');

my $badparams = join('|',@badparams);   # '|' or any other character not present in params

foreach my $par (4,5,6,7,'a','z','zzz')
{
    if ($badparams =~ /\b$par\b/)
    {
        print "$par is present\n";
    }
    else
    {
        print "$par is not present\n";
    }
}

आप संख्यात्मक प्रमुख रिक्त स्थान स्थिरता के लिए जाँच कर सकते हैं

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