2009-02-25 8 views
9

मैं एक पर्ल फ़ंक्शन को परिभाषित करना चाहता हूं (इसे "अंतर" कहते हैं) जो कमांड-लाइन तर्क पर निर्भर करता है। निम्नलिखित कोड काम नहीं करता:मैं पर्ल सबराउटिन को सशर्त रूप से परिभाषित कैसे कर सकता हूं?

if ("square" eq $ARGV[0]) {sub difference {return ($_[0] - $_[1]) ** 2}} 
elsif ("constant" eq $ARGV[0]) {sub difference {return 1}} 

ऐसा लगता है कि हालत नजरअंदाज कर दिया है, और इसलिए "अंतर" समारोह $ [0] ARGV के मूल्य की परवाह किए बिना दूसरी परिभाषा हो जाता है।

मैं समारोह में एक शर्त रख कर कोड काम कर सकते हैं:

sub difference { 
    if ("square" eq $ARGV[0]) {return ($_[0] - $_[1]) ** 2} 
    elsif ("constant" eq $ARGV[0]) {return 1} 
} 

लेकिन यह वास्तव में मेरा इरादा नहीं है - मैं शर्त की जरूरत नहीं है निष्पादन के दौरान हर बार मूल्यांकन किया जाना। मुझे बस कार्य की परिभाषा को प्रभावित करने के लिए एक तरीका चाहिए।

मेरे प्रश्न हैं:

  1. पहले निर्माण क्यों काम नहीं करता है?
  2. यह कोई त्रुटि क्यों नहीं देता है, या कुछ अन्य संकेत है कि कुछ गलत है?
  3. क्या पर्ल में सशर्त रूप से कार्यों को परिभाषित करने का कोई तरीका है?
+0

जब आप इसमें हों, तो आप यह सुनिश्चित करना चाहेंगे कि आप या तो ए) मरने से पहले अमान्य तर्क हैं या बी) यदि पहले दो कथनों में से कोई भी काम नहीं करता है तो तीसरी परिभाषा है। यदि आप कॉल करने और अंतर करने का प्रयास करते हैं तो यह बुरा होगा और यह वहां नहीं है। –

+1

जब आप "सख्त उपयोग नहीं करते हैं, चेतावनियों का उपयोग करें 'सभी';" ये आपके द्वारा चलाए जाने वाली समस्याओं के प्रकार हैं। – JDrago

उत्तर

24

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

sub square_difference { return ($_[0] - $_[1]) ** 2 } 
sub constant_difference { return 1 } 

my %lookup = (
    'square' => \&square_difference, 
    'constant' => \&constant_difference, 
); 

my $difference = $lookup{$ARGV[0]} || die "USAGE: $0 square|constant\n"; 
print &$difference(4, 1), "\n"; 

यह एक ही मूल दृष्टिकोण है, लेकिन मुझे लगता है कि इस वाक्य रचना आप और अधिक आसानी से आप प्रत्येक के और अधिक जोड़ने के रूप में एक सा सबरूटीन्स के लिए तर्क को मैप करने देगा: उदाहरण के लिए। ध्यान दें कि यदि आप उस तरह की चीज में हैं, तो Strategy Pattern पर यह एक भिन्नता है।

+0

मैं मानता हूं कि यह एक बेहतर (अधिक मजबूत) दृष्टिकोण है (हालांकि निरंतर_डिफरेंस को डिफॉल्ट करने के बजाए मरने से बेहतर आईएमएचओ भी बेहतर होगा)। –

+0

मैं लियोन टिमर्मन्स से सहमत हूं: डिफ़ॉल्ट मानना ​​बहुत बुरा हो सकता है। कॉन्फ़िगरेशन फ़ाइल से डिफ़ॉल्ट को आज़माने और पढ़ने का बुरा विचार नहीं हो सकता है, लेकिन अन्यथा अगर मैं निर्दिष्ट नहीं करता तो मैं मर जाऊंगा। –

+0

हालांकि मुझे यह पसंद है कि आप नामांकित सबराउटिन बनाते हैं और फिर उन्हें संदर्भ खींचते हैं। यह अज्ञात subroutines का उपयोग करने से कहीं अधिक सुरक्षित लगता है, खासकर यदि आप बाद में उन्हें ओवरराइट करना चाहते हैं। –

11

मैं व्यक्तिगत रूप से इस का एक बहुत नहीं किया है, लेकिन आप एक चर का उपयोग करने के सबरूटीन धारण करने के लिए चाहते हो सकता है:

my $difference; 
if ("square" eq $ARGV[0]) {$difference = sub {return ($_[0] - $_[1]) ** 2}} 
elsif ("constant" eq $ARGV[0]) {$difference = sub {return 1}} 

साथ कॉल:

&{ $difference }(args); 

या:

&$difference(args); 

या, जैसा कि लियोन टिमर्मन्स द्वारा सुझाया गया है:

$difference->(args); 

स्पष्टीकरण के बारे में थोड़ी - यह एक चर $difference बुलाया वाणी और, अपने परिवेश के आधार पर, यह सेट एक गुमनाम सबरूटीन के लिए एक संदर्भ धारण करने के लिए। इसलिए आपको subroutine को कॉल करने के लिए dereference$difference एक सबराउटिन (इसलिए & सामने) के रूप में है।

संपादित करें: कोड परीक्षण और काम करता है।

एक और संपादित करें:

यीशु, मैं इतना use ing strict और warnings है कि मैं भूल जाते हैं वे वैकल्पिक हैं करने के लिए इस्तेमाल कर रहा हूँ।

लेकिन गंभीरता से। हमेशा use strict; और use warnings;। इससे इस तरह की चीज़ों को पकड़ने में मदद मिलेगी, और आपको उपयोगी उपयोगी संदेश मिलेंगे जो बताते हैं कि क्या गलत है। strict और warnings की वजह से मुझे अपने जीवन में कभी भी डीबगर का उपयोग नहीं करना पड़ा - यह त्रुटि-जांच संदेश कितना अच्छा है। वे इस तरह की सभी चीजों को पकड़ लेंगे, और आपको के लिए उपयोगी संदेश भी देंगे, वे गलत हैं।

तो कृपया, जब भी आप कुछ लिखते हैं, कोई फर्क नहीं पड़ता कि कितना छोटा (जब तक यह obfuscated नहीं है), हमेशा use strict; और use warnings;

+0

इसके लिए बेहतर वाक्यविन्यास $ अंतर होगा -> (तर्क) –

+0

मुझे यह दृष्टिकोण सबसे अच्छा लगता है - यह देशी संदर्भ नोटेशन का उपयोग करता है और किसी अन्य हैश तालिका की आवश्यकता नहीं होती है। वास्तव में, इस दृष्टिकोण या हैश तालिका संदर्भ दृष्टिकोण के साथ, आप एक अतिरिक्त फ़ंक्शन परत जोड़कर फ़ंक्शन उपयोगकर्ता से जादू को पूरी तरह छुपा सकते हैं - उदा। $ फ़ंक्शन -> (तर्क) को उस फ़ंक्शन में लपेटें जिसका नाम सरल और आसान है: उप diff (args) { $ अंतर -> (तर्क); } अब आपको अपने मुख्य कार्यक्रम में तीर (या वैकल्पिक) नोटेशन का उपयोग करने की आवश्यकता नहीं है। – bearvarine

16

आपको बस इतना करना इस तरह प्राप्त किया जा सकता चाहते हैं क्या:

if ($ARGV[0] eq 'square') { 
    *difference = sub { return ($_[0] - $_[1]) ** 2 }; 
} 
elsif ($ARGV[0] eq 'constant') { 
    *difference = sub { return 1 }; 
} 
+0

एक टाइपग्लोब का उपयोग थोड़ा भारी कर्तव्य है, है ना? कोड रेफरी रखने के बजाय आप $ अंतर का उपयोग कर सकते हैं; आप इसका उपयोग करने के लिए और $ अंतर (arg1, arg2) का उपयोग करेंगे। –

+1

एक टाइपग्लोब ऐसा करने का तरीका है। हालांकि, मैं इसे एक ब्लॉक में डाल दूंगा। (असल में, मुझे लगता है कि "समस्या" ब्राइंडेड है, और इस तरह सशर्त रूप से चीजों को संकलित नहीं करेगा। लेकिन इसके साथ ...) – jrockway

+0

@jrockway - आप इसे BEGIN ब्लॉक में क्यों डाल देंगे? क्या यह किसी विशेष कारण के लिए जरूरी है या यह सिर्फ आपकी व्यक्तिगत शैली है? –

6

Subs संकलन समय पर परिभाषित कर रहे हैं -> यदि आप "उपयोग चेतावनी" सक्षम था, आप सबरूटीन परिभाषा के बारे में एक त्रुटि संदेश देखा होगा।

+0

हां, लेकिन रनटाइम पर subroutines को परिभाषित करने के तरीके हैं। दो पोस्ट उत्तर देखें। –

+0

hmmm, मेरा संपादन मिस रहा है ... आप रनटाइम पर असाइन कर सकते हैं - जो वास्तव में सुझाया गया है। लेकिन अनिवार्य रूप से, सब्स संकलन समय पर परिभाषित किया गया है ("eval" विकल्प को अनदेखा कर रहा है)। – Mathew

+0

आह ठीक है।"चेतावनियों का उपयोग" की सिफारिश करने के लिए +1। मैं भूल जाता हूं कि "सख्त" और "चेतावनियां" वैकल्पिक हैं। –

-2

फिर भी एक और तरीका है:

my $diffmode; 
BEGIN { $diffmode = $ARGV[0] } 
sub difference { 
    if ($diffmode eq 'square') { ($_[0] - $_[1]) ** 2 } 
    elsif ($diffmode eq 'constant') { 1 } 
    else { "It don't make no never mind" } 
} 
+0

क्या इसे संकलित समय पर अनुकूलित किया जाएगा, क्योंकि संकलन समय पर $ diffmode ज्ञात होगा? या यह सिर्फ उसके उत्तर में जैसा ही है? –

+0

नहीं, अनुकूलित नहीं किया गया है, लेकिन यह वैश्विक रहने पर अपरिवर्तित रहने पर निर्भर नहीं है। – ysth

0

कैसे कोड काम करने के लिए के लिए सभी सुझावों के लिए धन्यवाद। बस पूर्णता के लिए, मैं अपने प्रश्न के उच्च स्तरीय जवाब दूंगा।

  1. क्योंकि कार्यों संकलन समय पर परिभाषित कर रहे हैं, लेकिन शर्तों और/या कमांड लाइन तर्क रनटाइम पर मूल्यांकन किया जाता है पहले निर्माण काम नहीं करता। जब तक स्थिति का मूल्यांकन किया जाता है, नामित फ़ंक्शन को पहले ही परिभाषित किया जा चुका है।

  2. कंपाइलर "चेतावनियों का उपयोग" के साथ चेतावनी देता है, हालांकि एक प्रोग्रामर के लिए बहुत उपयोगी नहीं है जो 1 :-) से अनजान है, अर्थपूर्ण चेतावनी देने में कठिनाई यह है कि अगर किसी कथन के अंदर परिभाषित कार्य समझ सकते हैं यदि आप लिन टिमर्मन्स के सुझाव में, अगर कथन के भीतर फ़ंक्शन के साथ कुछ भी करते हैं। मूल कोड एक खाली अगर कथन के लिए संकलित करता है, और संकलक इनके बारे में चेतावनी देने के लिए सेट नहीं है।

  3. कड़ाई से बोलते हुए, सशर्त रूप से कार्यों को परिभाषित करना संभव नहीं है, लेकिन सशर्त रूप से कार्यों (rbright) या उपनाम (लियोन Timmermans) को कार्यों के लिए परिभाषित करना संभव है। सर्वसम्मति यह प्रतीत होती है कि संदर्भ उपनामों से बेहतर हैं, हालांकि मुझे पूरा यकीन नहीं है कि क्यों।

नोट 1: मूल्यांकन आदेश तब तक स्पष्ट नहीं है जब तक आप वास्तव में इस तरह की समस्या में भाग नहीं लेते; कोई एक पर्ल कल्पना कर सकता है जो संकलन समय पर स्थितियों का मूल्यांकन करेगा जब भी इसे सुरक्षित रूप से किया जा सके। स्पष्ट रूप से पर्ल ऐसा नहीं करता है, क्योंकि निम्न कोड भी एक परिभाषित सबराउटिन के बारे में चेतावनी देता है।

use warnings ; 
if (1) {sub jack {}} else {sub jack {}} 
+0

सशर्त रूप से कार्यों को परिभाषित करने के लिए संभव नहीं है * स्ट्रिंग eval * – ysth

+0

के बिना अगर (1) मामले में, perl अन्यथा अनुकूलित करता है, लेकिन यह केवल निष्पादन को प्रभावित करता है; दूसरी उप परिभाषा पहले ही हो चुकी है – ysth

+0

लियोन का कोड वास्तव में उपनाम नहीं बनाता है। यह सामान्य नाम की तरह प्रतीक तालिका में एक नामित उप स्थापित करता है। संदर्भ आमतौर पर पसंद किए जाते हैं लेकिन अपवाद हैं। जैसे मैंने एक तेज (एक्सएस) कार्यान्वयन और धीमी (शुद्ध पर्ल) पर फॉलबैक के बीच चयन करने के लिए टाइपग्लब्स का उपयोग किया है। –

1

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

Alias एक भूल गया मॉड्यूल है जो सख्त रखने के दौरान एक संदर्भ देने के लिए आवश्यक सभी जादू को लपेटता है।

use strict; 
use Alias; 

my $difference_method = $ARGV[0]; 
if("square" eq $difference_method) { 
    alias difference => sub { return ($_[0] - $_[1]) ** 2 }; 
} 
elsif("constant" eq $difference_method) { 
    alias difference => sub { return 1 }; 
} 
else { 
    die "Unknown difference method $difference_method"; 
} 

और अब difference($a, $b) काम करता है।

यदि आपको केवल अपने कोड के अंदर difference() पर कॉल करने की आवश्यकता है, यानी। आप इसे एक समारोह के रूप में निर्यात नहीं करने जा रहे हैं, मैं सिर्फ एक कोड संदर्भ का उपयोग करता हूं और एलियासिंग भूल जाता हूं।

my $difference_method = $ARGV[0]; 

my $Difference; 
if("square" eq $difference_method) { 
    $Difference => sub { return ($_[0] - $_[1]) ** 2 }; 
} 
elsif("constant" eq $difference_method) { 
    $Difference => sub { return 1 }; 
} 
else { 
    die "Unknown difference method $difference_method"; 
} 

$Difference->($a, $b); 

सशर्त बदलते क्या एक समारोह करता है का पालन करने के कोड कठिन और कम लचीला बनाता है, बस किसी भी वैश्विक पर व्यवहार को बदलने की तरह।

my $Difference_Method = $ARGV[0]; 

sub difference { 
    if($Difference_Method eq 'square') { 
     return ($_[0] - $_[1]) ** 2; 
    } 
    elsif($Difference_Method eq 'constant') { 
     return 1; 
    } 
    else { 
     die "Unknown difference method $Difference_Method"; 
    } 
} 

किसी भी समय आप प्रपत्र की एक सबरूटीन है ...

sub foo { 
    if($Global) { 
     ...do this... 
    } 
    else { 
     ...do that... 
    } 
} 

आप एक समस्या है: जब आपको लगता है कि आप सिर्फ इस अनुकूलित कर रहे इसे और अधिक स्पष्ट हो जाता है।

एलिसिंग & को हाथ से चिपकाने के बजाय बंद करने के दौरान रन टाइम पर समान समय उत्पन्न करने के लिए सबसे उपयोगी है। लेकिन यह एक और सवाल के लिए है।

+0

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

+1

वैश्विक चर के साथ एक ही समस्या। कोड का कोई भी भाग अंतर() को कॉल कर सकता है। अगले व्यक्ति को अंतर() परिवर्तन का एहसास नहीं हो सकता है। एक व्याख्यात्मक कोड का उपयोग करके $ अंतर को अंतर करके आप केवल कोड के ब्लॉक पर प्रभाव को सीमित करते हैं और आप इसे स्पष्ट करते हैं कि यह बदल सकता है। संकीर्ण क्षेत्रों का मतलब सरल कोड है। – Schwern