Perl's Special Variables

文章推薦指數: 80 %
投票人數:10人

One of the best ways to make your Perl code look more like … well, like Perl code – and not like C or BASIC or whatever you used before you were ... MORE: Togglenavigation Perl.com   ABOUT   AUTHORS   CATEGORIES #   TAGS Perl'sSpecialVariables Jun18,2004by DaveCross OneofthebestwaystomakeyourPerlcodelookmorelike…well,likePerlcode–andnotlikeCorBASICorwhateveryouusedbeforeyouwereintroducedtoPerl–istogettoknowtheinternalvariablesthatPerlusestocontrolvariousaspectsofyourprogram’sexecution. Inthisarticlewe’lltakealookatanumberofvariablesthatgiveyoufinercontroloveryourfileinputandoutput. CountingLines IdecidedtowritethisarticlebecauseIamconstantlyamazedbythenumberofpeoplewhodon’tknowabouttheexistenceof$..Istillseepeopleproducingcodethatlookslikethis: my$line_no=0; while(){ ++$line_no; unless(/someregex/){ warn"Errorinline$line_no\n"; next; } #processtherecordinsomeway } Forsomereason,manypeopleseemtocompletelymisstheexistenceof$.,whichisPerl’sinternalvariablethatkeepstrackofyourcurrentrecordnumber.Thecodeabovecanberewrittenas: while(){ unless(/someregex/){ warn"Errorinline$.\n"; next; } #processtherecordinsomeway } Iknowthatitdoesn’tactuallysaveyouverymuchtyping,butwhycreateanewvariableifyoudon’thaveto? Oneothernicewaytouse$.isinconjunctionwithPerl’s“flip-flop”operator(..).Whenusedinlistcontext,..isthelistconstructionoperator.Itbuildsalistofelementsbycalculatingalloftheitemsbetweengivenstartandendvalueslikethis: my@numbers=(1..1000); Butwhenyouusethisoperatorinascalarcontext(like,forexample,astheconditionofanifstatement),itsbehaviorchangescompletely.Thefirstoperand(theleft-handexpression)isevaluatedtoseeifitistrueorfalse.Ifitisfalsethentheoperatorreturnsfalseandnothinghappens.Ifitistrue,however,theoperatorreturnstrueandcontinuestoreturntrueonsubsequentcallsuntilthesecondoperand(theright-handexpression)returnstrue. Anexamplewillhopefullymakethisclearer.Supposeyouhaveafileandyouonlywanttoprocesscertainsectionsofit.Thesectionsthatyouwanttoprintareclearlymarkedwiththestring“!!START!!”atthestartand“!!END!!”attheend.Usingtheflip-flopoperatoryoucanwritecodelikethis: while(){ if(/!!START!!/../!!END!!/){ #processline } } Eachtimearoundtheloop,thecurrentlineischeckedbytheflip-flopoperator.Ifthelinedoesn’tmatch/!!START!!/thentheoperatorreturnsfalseandtheloopcontinues.Whenwereachthefirstlinethatmatches/!!START!!/thentheflip-flopoperatorreturnstrueandthecodeintheifblockisexecuted.Onsubsequentiterationsofthewhileloop,theflip-flopoperatorchecksformatchesagain/!!END!!/,butitcontinuestoreturntrueuntilitfindsamatch.Thismeansthatallofthelinesbetweenthe“!!START!!”and“!!END!!”markersareprocessed.Whenalinematches/!!END!!/thentheflip-flopoperatorreturnsfalseandstartscheckingagainstthefirstregexagain. Sowhatdoesallthishavetodowith$.?Well,there’sanotherpieceofmagiccodedintotheflip-flopoperator.Ifeitherofitsoperandsareconstantvaluesthentheyareconvertedtointegersandmatchedagainst$..Sotoprintoutjustthefirst10linesofafileyoucanwritecodelikethis: while(){ printif1..10; } Onefinalpointon$.,thereisonlyone$.variable.Ifyouarereadingfrommultiplefilehandlesthen$.containsthecurrentrecordnumberfromthemostrecentlyreadfilehandle.IfyouwantanythingmorecomplexthenyoucanusesomethinglikeIO::Fileobjectsforyourfilehandle.Theseobjectsallhaveaninput_line_numbermethod. TheFieldRecordSeparators Next,we’lllookat$/and$\whicharetheinputandoutputrecordseparatorsrespectively.Theycontrolwhatdefinesa“record”whenyouarereadingorwritingdata. Letmeexplainthatinabitmoredetail.RememberwhenyouwerefirstlearningPerlandyouwereintroducedtothefileinputoperator.Almostcertainlyyouweretoldthatreaddatafromthefileuptoandincludingthenextnewlinecharacter.Wellthat’snottrue.Well,itis,butit’sonlyaspecializedcase.Actuallyitreadsdatauptoandincludingthenextoccurrenceofwhateveriscurrentlyin$/-thefileinputseparator.Let’slookatanexample. Imagineyouhaveatextfilewhichcontainsamusingquotes.Orlyricsfromsongs.Orwhateveritisthatyouliketoputinyourrandomlygeneratedsignature.Thefilemightlooksomethinglikethis. Thisisthedefinitionofmylife %% Wearefartooyoungandclever %% Stabasorryheart Withyourfavoritefinger Herewehavethreequotesseparatedbyalinecontainingjustthestring%%.Howwouldyougoaboutreadinginthatfileaquoteatatime? Onesolutionwouldbetoreadthefilealineatatime,checkingtoseeifthenewlineisjustthestring%%.You’dneedtokeepavariablethatcontainscurrentquotethatyouarebuildingupandprocessacompletedquotewhenyoufindtheterminationstring.Oh,andyou’dneedtoremembertoprocessthelastquoteinthefileasthatdoesn’thaveaterminationstring(although,itmight!) AsimplersolutionwouldbetochangePerl’sideaofwhatconstitutesarecord.Wedothatbychangingthevalueof$/.Thedefaultvalueisanewlinecharacter-whichiswhy<...>usuallyreadsinalineatatime.Butwecansetittoanyvaluewelike.Wecandosomethinglikethis $/="%%\n"; while(){ chomp; print; } Noweachtimewecallthefileinputoperator,Perlreadsdatafromthefilehandleuntilitfinds%%\n(ortheendoffilemarker).Anewlineisnolongerseenasaspecialcharacter.Notice,however,thatthefileinputoperatoralwaysreturnsthenextrecordwiththefileinputseparatorstillattached.When$/hasitsdefaultvalueofanewlinecharacter,youknowthatyoucanremovethenewlinecharacterbycallingchomp.Wellitworksexactlythesamewaywhen$/hasothervalues.Itturnsoutthatchompdoesn’tjustremoveanewlinecharacter(that’sanother“simplification”thatyoufindinbeginnersbooks)itactuallyremoveswhateveristhecurrentvalueof$/.Soinoursamplecodeabove,thecalltochompisremovingthewholestring%%\n. ChangingPerl’sSpecialVariables BeforewegoonIjustneedtoalertyoutoonepossiblerepercussionofchangingthesevariableswheneveryouwant.Theproblemisthatmostofthesevariablesareforcedintothemainpackage.Thismeansthatwhenyouchangeoneofthesevariables,youarealteringthevalueeverywhereinyourprogram.Thisincludesanymodulesthatyouuseinyourprogram.Thereverseisalsotrue.Ifyou’rewritingamodulethatotherpeoplewilluseintheirprogramsandyouchangethevalueof$/insideit,thenyouhavechangedthevalueforalloftheremainingprogramexecution.Ihopeyoucanseenwhychangingvariableslike$/inonepartofyourprogramcanpotentiallyleadtohardtofindbugsinanotherpart. Soweneedtodowhatwecantoavoidthis.Yourfirstapproachmightbetoresetthevalueof$/afteryouhavefinishedwithit.Soyou’dwritecodelikethis. $/="%%\n"; while(){ chomp; print; } $/="\n"; Theproblemwiththisisyoucan’tbesurethat$/contained\nbeforeyoustartedfiddlingwithit.Someoneelsemighthavechangeditbeforeyourcodewasreached.Sothenextattemptmightlooklikethis. $old_input_rec_sep=$/; $/="%%\n"; while(){ chomp; print; } $/=$old_input_rec_sep; Thiscodeworksanddoesn’thavethebugthatwe’retryingtoavoidbutthere’sanotherwaythatlookscleaner.Rememberthelocalfunctionthatyouusedtodeclarelocalvariablesuntilsomeonetoldyouthatyoushouldusemyinstead?Wellthisisoneofthefewplaceswhereyoucanuselocaltogreateffect. It’sgenerallyacknowledgedthatlocalisbadlynamed.Thenamedoesn’tdescribewhatthefunctiondoes.InPerl6thefunctionislikelytoberenamedtotempasthat’safarbetterdescriptionofwhatitdoes-itcreatesatemporaryvariablewiththesamenameasanexistingvariableandrestorestheoriginalvariablewhentheprogramleavestheinnermostenclosingblock.Thismeansthatwecanwriteourcodelikethis. { local$/="%%\n"; while(){ chomp; print; } } We’veenclosedallofthecodeinanotherpairofbracestocreateanakedblock.Codeblocksareusuallyassociatedwithloops,conditionalsorsubroutines,butinPerltheydon’tneedtobe.Youcanintroduceanewblockwheneveryouwant.Here,we’veintroducedablockpurelytodelimittheareawherewewant$/tohaveanewvalue.Wethenuselocaltostoretheold$/variablesomewherewhereitcan’tbedisturbedandsetournewversionofthevariableto%%\n.Wecanthendowhateverwewantinthecodeblockandwhenweexitfromtheblock,Perlautomaticallyrestorestheoriginalcopyof$/andweneverneededtoknowwhatitwassetto. Forallthisreason,it’sgoodpracticetoneverchangeoneofPerl’sinternalvariablesunlessitislocalizedinablock. OtherValuesFor$/ Thereareafewspecialvaluesthatyoucangive$/whichturnoninterestingbehaviours.Thefirstoftheseissettingittoundef.Thisturnson“slurpmode”andthenexttimeyoureadfromafilehandleyouwillgetalloftheremainingdatarightuptotheendoffilemarker.Thismeansthatyoucanreadawholefileinusingcodelikethis. my$file=do{local$/;}; Adoblockreturnsthevalueofthelastexpressionevaluatedwithinit,whichinthiscaseisthefileinputoperator.Andas$/hasbeensettoundefitreturnsthewholefile.Noticethatwedon’tevenneedtoexplicitlyset$/toundefasallPerlvariablesareinitializedtoundefwhentheyarecreated. Thereisabigdifferencebetweensetting$/toundefandsettingittoanemptystring.Settingittoanemptystringturnson“paragraph”mode.Inthismodeeachrecordisaparagraphoftextterminatedbyoneormoreemptylines.Youmightthinkthatthiseffectcanbemimickedbysetting$/to\n\n,butthesubtledifferenceisthatparagraphmodeactsasthought$/hadbeensetto\n\n+(althoughyoucan’tactuallyset$/equaltoaregularexpression.) Thefinalspecialvalueistoset$/toeitherareferencetoascalarvariablethatholdsaninteger,ortoareferencetoanintegerconstant.Inthesecasesthenextreadfromafilehandlewillreaduptothatnumberofbytes(Isay“upto”becauseattheendofthefiletheremightnotbeenoughdatalefttogiveyou).Soyoureadafile2Kbatatimeandyoucandothis. { local$/=\2048; while(){ #$_containsthenext2048bytesfromFILE } } $/and$. Notethatchanging$/altersPerl’sdefinitionofarecordandthereforeitaltersthebehaviorof$..$.doesn’tactuallycontainthecurrentlinenumber,itcontainsthecurrentrecordnumber.Soinourquotesexampleabove,$.willbeincrementedforeachquotethatyoureadfromthefilehandle. WhatAbout$\? ManyparagraphsbackImentionedboth$/and$\asbeingtheinputandoutputrecordseparators.ButsincethenI’vejustgoneonabout$/.Whathappenedto$\? Well,tobehonest,$\isn’tanywherenearasusefulas$/.Itcontainsastringthatisprintedattheendofeverycalltoprint.Itsdefaultvalueistheemptystring,sonothinggetsaddedtodatathatyoudisplaywithprint.Butif,forexample,youlongedforthedaysofPascalyoucouldwriteaprintlnfunctionlikethis. subprintln{ local$\="\n"; print@_; } Theneverytimeyoucalledprintln,alloftheargumentswouldbeprintedfollowedbyanewline. OtherPrintVariables ThenexttwovariablesthatIwanttodiscussareveryeasilyconfusedalthoughtheydocompletelydifferentthings.Toillustratethem,considerthefollowingcode. my@arr=(1,2,3); print@arr; print"@arr"; Now,withoutlookingitupdoyouknowwhatthedifferenceisbetweentheoutputfromthetwocallstoprint? Theansweristhatthefirstoneprintsthethreeelementsofthearraywithnothingseparatingthem(likethis-123)whereasthesecondoneprintstheelementsseparatedbyspaces(likethis-123).Whyisthereadifference? Thekeytounderstandingitistolookatexactlywhatisbeingpassedtoprintineachcase.Inthefirstcaseprintispassedanarray.Perlunrollsthatarrayintoalistandprintactuallyseesthethreeelementsofthearrayasseparatearguments.Inthesecondcase,thearrayisinterpolatedintoadoublequotedstringbeforeprintseesit.Thatinterpolationhasnothingatalltodowiththecalltoprint.Exactlythesameprocesswouldtakeplaceif,forexample,wedidsomethinglikethis. my$string="@arr"; print$string; Sointhesecondcase,theprintfunctiononlyseesoneargument.Thefactthatitistheresultsofinterpolatinganarrayindoublequoteshasnoeffectonhowprinttreatsthestring. Wethereforehavetwocases.Whenprintreceivesanumberofargumentsitprintsthemoutwithnospacesbetweenthem.Andwhenanarrayisinterpolatedindoublequotesitisexpandedwithspacesbetweentheindividualelements.Thesetwocasesarecompletelyunrelated,butfromourfirstexampleaboveit’seasytoseehowpeoplecangetthemconfused. Ofcourse,Perlallowsustochangethesebehaviorsifwewantto.Thestringthatisprintedbetweentheargumentspassedtoprintisstoredinavariablecalled$,(becauseyouuseacommatoseparatearguments).Aswe’veseen,thedefaultvalueforthatisanemptystringbutitcan,ofcourse,bechanged. my@arr=(1,2,3); { local$,=','; print@arr; } Thiscodeprintsthestring1,2,3. Thestringthatseparatestheelementsofanarraywhenexpandedinadoublequotedstringisstoredin$".Onceagain,it’ssimpletochangeittoadifferentvalue. my@arr=(1,2,3); { local$"='+'; print"@arr"; } Thiscodeprints1+2+3". Ofcourse,$"doesn’tnecessarilyhavetousedinconjunctionwithaprintstatement.Youcanuseitanywherethatyouhaveanarrayinadoubledquotedstring.Anditdoesn’tjustworkforarrays.Arrayandhashslicesworkjustaswell. my%hash=(one=>1,two=>2,three=>3); { local$"='



請為這篇文章評分?