2009-09-22 8 views
6

कोड मैंने लिखा रूप में नीचे है:मेरी पर्ल मानचित्र सूची केवल 1 क्यों है?

#!/usr/bin/perl 

my @input = ("a.txt" , "b.txt" , "c.txt") ; 
my @output = map { $_ =~ s/\..*$// } @input ; 

print @output ; 

मेरे इरादा विस्तार सरणी @output में संग्रहीत बिना फ़ाइल नाम देना है। लेकिन इसके बजाय यह नहीं बल्कि @output में बदली हुई फ़ाइल नाम से s/// द्वारा दिए गए मान संग्रहीत करता है, तो परिणाम

1 
1 
1 

तो इस स्थिति के तहत map उपयोग करने के लिए सही तरीका क्या है की तरह लग रहा है?

उत्तर

14

ठीक है, सबसे पहले, शायद आपके पास $_ =~ s/\..*$// होना चाहिए - अपने उदाहरण में अनुपलब्ध s पर ध्यान दें। इसके अलावा, आप शायद mapgrep का मतलब नहीं है।

दूसरा, जो आप चाहते हैं वह नहीं करता है। यह वास्तव में @input संशोधित करता है! grep (और map, और कई अन्य स्थानों के अंदर), $_ वास्तव में प्रत्येक मान के लिए अलियाकृत है। तो आप वास्तव में मूल्य बदल रहे हैं।

यह भी ध्यान रखें कि पैटर्न मिलान मिलान मूल्य वापस नहीं करता है; यह सच होता है (यदि कोई मैच है) या झूठा (यदि नहीं है)। यह सब 1 है जिसे आप देख रहे हैं।

इसके बजाय, कुछ इस तरह करते हैं:

my @output = map { 
    (my $foo = $_) =~ s/\..*$//; 
    $foo; 
} @input ; 

पहले प्रतियां $foo करने के लिए $_, और फिर $foo संशोधित करता है। फिर, यह संशोधित मान देता है ($foo में संग्रहीत)। आप return $foo का उपयोग नहीं कर सकते, क्योंकि यह एक ब्लॉक है, एक subroutine नहीं।

+0

@derobert: लापता 'एक संपादन त्रुटि है, मैंने इसे ठीक कर दिया है।और मैंने आपके समाधान का परीक्षण किया है और यह काम करता है! –

+0

यह सुनकर खुशी हुई कि यह काम करता है। – derobert

+4

सूची :: अधिक उपयोग http://search.cpan.org/perldoc/List::MoreUtils 'लागू' प्रदान करता है जो इस तरह की चीज़ के लिए बिल्कुल सही है। यह 'मानचित्र' की तरह काम करता है, लेकिन सरणी तर्क में मानों को परिवर्तित नहीं करेगा। 'सूची का उपयोग करें :: MoreUtils' लागू करें '; मेरा @output = लागू करें {s /\..*$//} @ इनपुट; ' – daotoad

2

आप प्रतिस्थापन के लिए 'एस' खो रहे हैं।

$_ =~ /\..*$// 

$_ =~ s/\..*$// 

इसके अलावा, आप बनाने के लिए बेहतर अपने रेगुलर एक्सप्रेशन के रूप में s/\.[^\.]*$// उपयोग करने के लिए हो सकता है किया जाना चाहिए तुम सिर्फ विस्तार भी जब फ़ाइल नाम एक शामिल अवश्य निकाल दें '।' (डॉट) चरित्र।

+0

@ निखिल: हाँ, आपका रेगेक्स बेहतर है, धन्यवाद। –

0

आपके कोड नमूने में मैच ऑपरेटर में s गुम है। उसके अलावा, यह मेरे लिए ठीक काम किया:

$, = "\n"; 
my @input = ("a.txt" , "b.txt" , "c.txt"); 
my @output = grep { $_ =~ s/\..*$// } @input; 
print @output; 

आउटपुट है:

 
a 
b 
c 
+2

एक 'प्रिंट @ इनपुट' करें और ध्यान दें कि आपका कोड 'इनपुट' कैसे बदलता है, जो अप्रत्याशित और बहुत अवांछित है। – derobert

+0

यह शायद सच है। – bobbymcr

7

$_ सूची के मूल्यों aliasing की समस्या पहले से ही चर्चा की गई थी।

लेकिन और क्या है: आपके प्रश्न का शीर्षक स्पष्ट रूप से "मानचित्र" कहता है, लेकिन आपका कोड grep का उपयोग करता है, हालांकि ऐसा लगता है कि इसे वास्तव में मानचित्र का उपयोग करना चाहिए।

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

map दूसरी ओर सूची तर्क के तत्वों को बदलने के लिए अभिव्यक्ति या ब्लॉक तर्क का उपयोग करता है जो मूल की रूपांतरित परिवर्तनों वाली एक नई सूची लौटाता है।

इस प्रकार आपकी समस्या को इस तरह के कोड के साथ हल किया जा सकता:

@output = map { m/(.+)\.[^\.]+/ ? $1 : $_ } @input; 

इस फ़ाइल का नाम है जिसमें एक्सटेंशन नहीं है के भाग से मेल और मूल्यांकन का एक परिणाम के रूप में यह वापसी या मूल नाम वापस आ जाएगी अगर कोई विस्तार नहीं है।

1

derobert आपको @input से @output पर मैपिंग का सही तरीका दिखाता है।

मैं, तथापि, File::Basename का उपयोग कर की सिफारिश करेंगे:

#!/usr/bin/perl 

use strict; 
use warnings; 

use File::Basename; 

my @input = qw(a.1.txt b.txt c.txt); 
my @output = map { scalar fileparse($_, qr/\.[^.]*/) } @input ; 

use Data::Dumper; 
print Dumper \@output; 

आउटपुट:

 
C:\Temp> h 
$VAR1 = [ 
      'a.1', 
      'b', 
      'c' 
     ]; 
12
उन उत्तरों की सभी में से

, कोई भी बस ने कहा कि map रिटर्न पिछले का परिणाम मूल्यांकन अभिव्यक्ति। जो भी आप आखिरी करते हैं वह बात है (या चीजें) map रिटर्न। यह सिर्फ एक subroutine या do परिणाम की अपनी अंतिम मूल्यांकन अभिव्यक्ति की तरह लौट रहा है।

पर्ल v5.14 गैर-विनाशकारी प्रतिस्थापन जोड़ता है, जिसे मैं Use the /r substitution flag to work on a copy में लिखता हूं। प्रतिस्थापन की संख्या वापस करने के बजाय, यह संशोधित प्रति वापस देता है।

my @output = map { s/\..*$//r } @input; 

ध्यान दें कि आप के बाद से है कि डिफ़ॉल्ट विषय है बाध्यकारी ऑपरेटर के साथ $_ उपयोग करने की आवश्यकता नहीं है: /r ध्वज का उपयोग करें।

+2

'/ r' ध्वज को इंगित करने के लिए धन्यवाद, जिसे मैं अनजान था। –

1

जैसा कि बताया गया है, एस /// प्रदर्शन किए गए प्रतिस्थापन की संख्या देता है, और नक्शा प्रत्येक पुनरावृत्ति से मूल्यांकन की गई अंतिम अभिव्यक्ति देता है, इसलिए आपका नक्शा सभी 1 लौटाता है। एक तरह से पूरा करने के लिए आप क्या चाहते है:

s/\..*$// for my @output = @input; 

एक और तरीका है समस्या से Algorithm::Loops

+0

यह थोड़ा सा विवरण है जो आपको पर्ल में ले जाता है, एक अच्छा कारण है, जब तक कि आपको लगता है कि आप पूरी तरह से भाषा को समझते हैं, चीजों को पागल होने पर मॉड्यूल के साथ रहना चाहिए। – osirisgothra

0

फ़िल्टर उपयोग करने के लिए है: दोनों the s/../.../ operator और Perl's map जरूरी हैं, प्रत्येक इनपुट आइटम को संशोधित करना चाहते हैं जिसकी आप अपेक्षा कर; पर्ल वास्तव में कार्यात्मक map के लिए एक अंतर्निहित नहीं है जो इनपुट को संशोधित किए बिना इसके परिणाम उत्पन्न करता है।

#!/usr/bin/perl 

my @input = ("a.txt" , "b.txt" , "c.txt") ; 
my @output = map { s/\..*$//r } @input ; 

print join(' ', @output), "\n"; 

सामान्य समाधान (derobert ने सुझाव दिया) List::MoreUtils::apply उपयोग करने के लिए है:

जब s का उपयोग कर, एक विकल्प r संशोधक संलग्न करने के लिए है

#!/usr/bin/perl 

use List::MoreUtils qw(apply); 

my @input = ("a.txt" , "b.txt" , "c.txt") ; 
my @output = apply { s/\..*$// } @input ; 

print join(' ', @output), "\n"; 

या में इसकी परिभाषा कॉपी अपने कोड:

sub apply (&@) { 
    my $action = shift; 
    &$action foreach my @values = @_; 
    wantarray ? @values : $values[-1]; 
} 
संबंधित मुद्दे