Advanced Perl Programming, 2nd Editionsoftouch.on.ca/kb/data/Advanced Perl Programming. 2nd...
Transcript of Advanced Perl Programming, 2nd Editionsoftouch.on.ca/kb/data/Advanced Perl Programming. 2nd...
-
AdvancedPerlProgramming,2ndEditionBySimonCozens...............................................Publisher:O'ReillyPubDate:June2005ISBN:0-596-00456-7Pages:304
TableofContents|Index
Withaworldwidecommunityofusersandmorethanamilliondedicatedprogrammers,Perlhasproventobethemosteffectivelanguageforthelatesttrendsincomputingandbusiness.
Everyprogrammermustkeepupwiththelatesttoolsandtechniques.ThisupdatedversionofAdvancedPerlProgrammingfromO'ReillygivesyoutheessentialknowledgeofthemodernPerlprogrammer.WhateveryourcurrentlevelofPerlexpertise,thisbookwillhelpyoupushyourskillstothenextlevelandbecomeamoreaccomplishedprogrammer.
O'Reilly'smosthigh-levelPerltutorialtodate,AdvancedPerlProgramming,SecondEditionteachesyouallthecomplextechniquesforproduction-readyPerlprograms.Thiscompletelyupdatedguideclearlyexplainsconceptssuchasintrospection,overridingbuilt-ins,extendingPerl'sobject-orientedmodel,andtestingyourcodeforgreaterstability.
Othertopicsinclude:
Complexdatastructures
Parsing
Templatingtoolkits
Workingwithnaturallanguagedata
Unicode
InteractionwithCandotherlanguages
Inaddition,thisguidedemystifiesoncecomplextopicslikeobject-relationalmappingandevent-baseddevelopment-armingyouwitheverythingyouneedtocompletelyupgradeyourskills.
PraisefortheSecondEdition:
-
"Sometimesthebiggesthurdletoproblemsolvingisn'tthesubjectitselfbutratherthesheernumberofmodulesPerlprovides.AdvancedPerlProgrammingwalksyouthroughPerl'sTMTOWTDI("There'sMoreThanOneWayToDoIt")forest,explainingandcomparingthebestmodulesforeachtasksoyoucanintelligentlyapplytheminavarietyofsituations."--RoccoCaputo,leaddeveloperofPOE
"IthasbeensaidthatsufficientlyadvancedPerlcodeisindistinguishablefrommagic.Thisbookofspellsgoesalongwaytounlockingthosesecrets.IthasthepowertotransformthemosthumbleprogrammerintoaPerlwizard."--AndyWardley
"Theinformationhereisn'ttheoretical.Itpresentstoolsandtechniquesforsolvingrealproblemscleanlyandelegantly."--Curtis'Ovid'Poe
"AdvancedPerlProgrammingcollectshard-earnedknowledgefromsomeofthebestprogrammersinthePerlcommunity,andexplainsitinawaythatevennovicescanapplyimmediately."--chromatic,EditorofPerl.com
-
AdvancedPerlProgramming,2ndEditionBySimonCozens...............................................Publisher:O'ReillyPubDate:June2005ISBN:0-596-00456-7Pages:304
TableofContents|Index
Copyright Preface Audience Contents ConventionsUsedinThisBook UsingCodeExamples We'dLiketoHearfromYou Safari®Enabled Acknowledgments Chapter1.AdvancedTechniques Section1.1.Introspection Section1.2.MessingwiththeClassModel Section1.3.UnexpectedCode Section1.4.Conclusion Chapter2.ParsingTechniques Section2.1.Parse::RecDescentGrammars Section2.2.Parse::Yapp Section2.3.OtherParsingTechniques Section2.4.Conclusion Chapter3.TemplatingTools Section3.1.FormatsandText::Autoformat Section3.2.Text::Template Section3.3.HTML::Template Section3.4.HTML::Mason Section3.5.TemplateToolkit
-
Section3.6.AxKit Section3.7.Conclusion Chapter4.Objects,Databases,andApplications Section4.1.BeyondFlatFiles Section4.2.ObjectSerialization Section4.3.ObjectDatabases Section4.4.DatabaseAbstraction Section4.5.PracticalUsesinWebApplications Section4.6.Conclusion Chapter5.NaturalLanguageTools Section5.1.PerlandNaturalLanguages Section5.2.HandlingEnglishText Section5.3.ModulesforParsingEnglish Section5.4.CategorizationandExtraction Section5.5.Conclusion Chapter6.PerlandUnicode Section6.1.Terminology Section6.2.WhatIsUnicode? Section6.3.UnicodeTransformationFormats Section6.4.HandlingUTF-8Data Section6.5.Encode Section6.6.UnicodeforXSAuthors Section6.7.Conclusion Chapter7.POE Section7.1.ProgramminginanEvent-DrivenEnvironment Section7.2.Top-LevelPieces:Components Section7.3.Conclusion Chapter8.Testing Section8.1.Test::Simple Section8.2.Test::More Section8.3.Test::Harness Section8.4.Test::Builder Section8.5.Test::Builder::Tester Section8.6.KeepingTestsandCodeTogether Section8.7.UnitTests Section8.8.Conclusion Chapter9.InlineExtensions Section9.1.SimpleInline::C Section9.2.MoreComplexTaskswithInline::C
-
Section9.3.Inline::EverythingElse Section9.4.Conclusion Chapter10.FunwithPerl Section10.1.Obfuscation Section10.2.JustAnotherPerlHacker Section10.3.PerlGolf Section10.4.PerlPoetry Section10.5.Acme::* Section10.6.Conclusion Colophon AbouttheAuthor Colophon Index
-
AdvancedPerlProgramming,SecondEdition
bySimonCozens
Copyright©2005,1997O'ReillyMedia,Inc.Allrightsreserved.
PrintedintheUnitedStatesofAmerica.
PublishedbyO'ReillyMedia,Inc.,1005GravensteinHighwayNorth,Sebastopol,CA95472.
O'Reillybooksmaybepurchasedforeducational,business,orsalespromotionaluse.Onlineeditionsarealsoavailableformosttitles(safari.oreilly.com).Formoreinformation,contactourcorporate/institutionalsalesdepartment:(800)[email protected].
Editor: AllisonRandal
ProductionEditor: DarrenKelly
CoverDesigner: EdieFreedman
InteriorDesigner: DavidFutato
ProductionServices: nSight,Inc.
PrintingHistory:
August1997: FirstEdition.
June2005: SecondEdition.
NutshellHandbook,theNutshellHandbooklogo,andthe
mailto:[email protected]
-
O'ReillylogoareregisteredtrademarksofO'ReillyMedia,Inc.AdvancedPerlProgramming,theimageofaofablackleopard,andrelatedtradedressaretrademarksofO'ReillyMedia,Inc.
Manyofthedesignationsusedbymanufacturersandsellerstodistinguishtheirproductsareclaimedastrademarks.Wherethosedesignationsappearinthisbook,andO'ReillyMedia,Inc.wasawareofatrademarkclaim,thedesignationshavebeenprintedincapsorinitialcaps.
Whileeveryprecautionhasbeentakeninthepreparationofthisbook,thepublisherandauthorassumenoresponsibilityforerrorsoromissions,orfordamagesresultingfromtheuseoftheinformationcontainedherein.
ISBN:0-596-00456-7
[M]
-
PrefaceItwasallNathanTorkington'sfault.OurAntipodeanprogrammer,editor,andO'ReillyconferencesupremofriendaskedmetoupdatetheoriginalAdvancedPerlProgrammingwaybackin2002.
ThePerlworldhadchangeddrasticallyinthefiveyearssincethepublicationofthefirstedition,anditcontinuestochange.Particularly,we'veseenashiftawayfromtechniquesandtowardresourcesfromdoingthingsyourselfwithPerltousingwhatotherpeoplehavedonewithPerl.Inessence,advancedPerlprogramminghasbecomemoreamatterofknowingwheretofindwhatyouneedontheCPAN,[*]ratherthanamatterofknowingwhattodo.
[*]TheComprehensivePerlArchiveNetwork(http://www.cpan.org)istheprimaryresourceforuser-contributedPerlcode.
Perlchangedinotherways,too:theannouncementofPerl6in2000ironicallycausedarenewedinterestinPerl5,withpeoplestretchingPerlinnewandinterestingdirectionstoimplementsomeoftheideasandblue-skiesthinkingaboutPerl6.Contrarytowhatweallthoughtbackthen,farfromkillingoffPerl5,Perl6'sdevelopmenthasmadeitstrongerandensureditwillbearoundlonger.
SoitwasinthiscontextthatitmadesensetoupdateAdvancedPerlProgrammingtoreflectthechangesinPerlandintheCPAN.WealsowantedtheneweditiontobemoreinthespiritofPerltofocusonhowtoachievepracticaltaskswithaminimumoffuss.Thisiswhyweputtogetherchaptersonparsingtechniques,ondealingwithnaturallanguagedocuments,ontestingyourcode,andsoon.
Butthisbookisjustabeginning;howevertemptingitwasto
http://www.cpan.org
-
trytogetdowneverythingIeverwantedtosayaboutPerl,itjustwasn'tpossible.First,becausePerlusagecoverssuchawidespreadontheCPAN,thereareready-mademodulesforfoldingDNAsequences,payingbillsonline,checkingtheweather,andplayingpoker.Andmorearebeingaddedeveryday,fasterthananyauthorcankeepup.Second,aswe'vementioned,becausePerlischanging.Idon'tknowwhatthenextbigadvanceinPerlwillbe;Icanonlytakeyouthroughsomeofthemoreimportanttechniquesandresourcesavailableatthemoment.
Hopefully,though,attheendofthisbookyou'llhaveagoodideaofhowtousewhat'savailable,howyoucansaveyourselftimeandeffortbyusingPerlandthePerlresourcesavailabletogetyourjobdone,andhowyoucanbereadytouseandintegratewhateverdevelopmentscomedowntheline.
InthewordsofLarryWall,mayyoudogoodmagicwithPerl!
-
Audience
Ifyou'vereadLearningPerlandProgrammingPerlandwonderwheretogofromthere,thisbookisforyou.It'llhelpyouclimbtothenextlevelofPerlwisdom.Ifyou'vebeenprogramminginPerlforyears,you'llstillfindnumerouspracticaltoolsandtechniquestohelpyousolveyoureverydayproblems.
-
Contents
Chapter1,AdvancedTechniques,introducesafewcommontricksadvancedPerlprogrammersusewithexamplesfrompopularPerlmodules.
Chapter2,ParsingTechniques,coversparsingirregularorunstructureddatawithParse::RecDescentandParse::Yapp,plusparsingHTMLandXML.
Chapter3,TemplatingTools,detailssomeofthemostcommontoolsfortemplatingandwhentousethem,includingformats,Text::Template,HTML::Template,HTML::Mason,andtheTemplateToolkit.
Chapter4,Objects,Databases,andApplications,explainsvariouswaystoefficientlystoreandretrievecomplexdatausingobjectsaconceptcommonlycalledobject-relationalmapping.
Chapter5,NaturalLanguageTools,showssomeofthewaysPerlcanmanipulatenaturallanguagedata:inflections,conversions,parsing,extraction,andBayesiananalysis.
Chapter6,PerlandUnicode,reviewssomeoftheproblemsandsolutionstomakethemostofPerl'sUnicodesupport.
Chapter7,POE,looksatthepopularPerlevent-basedenvironmentfortaskscheduling,multitasking,andnon-blockingI/Ocode.
Chapter8,Testing,coverstheessentialsoftestingyourcode.
Chapter9,InlineExtensions,talksabouthowtoextendPerlbywritingcodeinotherlanguages,usingtheInline::*modules.
Chapter10,FunwithPerl,closesonalighternotewithafew
-
recreational(andeducational)usesofPerl.
-
ConventionsUsedinThisBook
Thefollowingtypographicalconventionsareusedinthisbook:
Plaintext
Indicatesmenutitles,menuoptions,menubuttons,andkeyboardaccelerators(suchasAltandCtrl).
Italic
Indicatesnewterms,URLs,emailaddresses,filenames,fileextensions,pathnames,directories,andUnixutilities.
Constantwidth
Indicatescommands,options,switches,variables,attributes,keys,functions,classes,namespaces,methods,modules,parameters,values,XMLtags,HTMLtags,thecontentsoffiles,ortheoutputfromcommands.
Constantwidthbold
Showscommandsorothertextthatshouldbetypedliterallybytheuser.
Constantwidthitalic
-
Showstextthatshouldbereplacedwithuser-suppliedvalues.
Thisiconsignifiesatip,suggestion,orgeneralnote.
Thisiconindicatesawarningorcaution.
-
UsingCodeExamples
Thisbookisheretohelpyougetyourjobdone.Ingeneral,youmayusethecodeinthisbookinyourprogramsanddocumentation.Youdonotneedtocontactusforpermissionunlessyou'rereproducingasignificantportionofthecode.Forexample,writingaprogramthatusesseveralchunksofcodefromthisbookdoesnotrequirepermission.SellingordistributingaCD-ROMofexamplesfromO'Reillybooksdoesrequirepermission.Answeringaquestionbycitingthisbookandquotingexamplecodedoesnotrequirepermission.Incorporatingasignificantamountofexamplecodefromthisbookintoyourproduct'sdocumentationdoesrequirepermission.
Weappreciate,butdonotrequire,attribution.Anattributionusuallyincludesthetitle,author,publisher,andISBN.Forexample:"AdvancedPerlProgramming,SecondEditionbySimonCozens.Copyright2005O'ReillyMedia,Inc.0-596-00456-7."
Ifyoufeelyouruseofcodeexamplesfallsoutsidefairuseorthepermissiongivenabove,[email protected].
mailto:[email protected]
-
We'dLiketoHearfromYou
Pleaseaddresscommentsandquestionsconcerningthisbooktothepublisher:
O'ReillyMedia1005GravensteinHighwayNorthSebastopol,CA95472(800)998-9938(intheUnitedStatesorCanada)(707)829-0515(internationalorlocal)(707)829-0104(fax)
Wehaveawebpageforthisbook,wherewelisterrata,examples,andanyadditionalinformation.Youcanaccessthispageat:
http://www.oreilly.com/catalog/advperl2/
Tocommentorasktechnicalquestionsaboutthisbook,sendemailto:
Formoreinformationaboutourbooks,conferences,ResourceCenters,andtheO'ReillyNetwork,seeourwebsiteat:
http://www.oreilly.com
http://www.oreilly.com/catalog/advperl2/mailto:[email protected]://www.oreilly.com
-
Safari®Enabled
WhenyouseeaSafariEnabledicononthecoverofyourfavoritetechnologybook,thatmeansthebookisavailableonlinethroughtheO'ReillyNetworkSafariBookshelf.
Safarioffersasolutionthat'sbetterthane-books.It'savirtuallibrarythatletsyoueasilysearchthousandsoftoptechbooks,cutandpastecodesamples,downloadchapters,andfindquickanswerswhenyouneedthemostaccurate,currentinformation.Tryitforfreeathttp://safari.oreilly.com.
http://safari.oreilly.com
-
Acknowledgments
I'vealreadyblamedNatTorkingtonforcommissioningthisbook;Ishouldthankhimaswell.Asmuchaswritingabookcanbefun,thisonehasbeen.Ithascertainlybeenhelpedbymyeditors,beginningwithNatandTatianaApandi,andendingwiththehugelytalentedAllisonRandal,whohasalmostsingle-handedlycorrectedcode,collatedcomments,andconvertedmyramblingthoughtsintosomethingpublishable.TheproductionteamatO'Reillydeservesaspecialmention,ifonlybecauseofthetortureIputthemthroughinhavingachapteronUnicode.
Allisonalsoroundedupagreatcrewofhighlyknowledgeablereviewers:mythankstoTonyBowden,PhilippeBruhat,SeanBurke,PiersCawley,NicholasClark,JamesDuncan,RafaelGarcia-Suarez,ThomasKlausner,TomMcTighe,CurtisPoe,chromatic,andAndyWardley.
Andfinally,thereareafewpeopleI'dliketothankpersonally:thankstoHeatherLang,GraemeEverist,andJulietHumphreyforputtingupwithmelastyear,andtoJillFordandtherestofhergroupatAllNationsChristianCollegewhohavetoputupwithmenow.TonyBowdentaughtmemoreaboutgoodPerlprogrammingthaneitherofuswouldprobablyadmit,andSimonPonsonbytaughtmemoreabouteverythingelsethanherealises.ThankstoAlandJamieforbeingthere,andtoMalcolmandCarolineMacdonaldandNorikoandAkioKawamuraforlaunchingmeonthecurrentexcitingstageofmylife.
-
Chapter1.AdvancedTechniquesOnceyouhavereadtheCamelBook(ProgrammingPerl),oranyothergoodPerltutorial,youknowalmostallofthelanguage.Therearenosecretkeywords,noothermagicsigilsthatturnonPerl'sadvancedmodeandrevealhiddenfeatures.Inonesense,thisbookisnotgoingtotellyouanythingnewaboutthePerllanguage.
WhatcanItellyou,then?Iusedtobeastudentofmusic.Musicisverysimple.Thereare12possiblenotesinthescaleofWesternmusic,althoughsomeofthemostwonderfulmelodiesintheworldonlyuse,atmost,eightofthem.Therearearoundfourdifferentdurationsofanoteusedincommonmelodies.Thereisn'tamassivemusicalvocabularytochoosefrom.AndmusichasbeenaroundagooddeallongerthanPerl.Iusedtowonderwhetherornotallthepossibledecentmelodieswouldsoonbefiguredout.SometimesIlistentotheTop10andthinkIwasprobablyrightbackthen.
Butofcourseit'sabitmorecomplicatedthanthat.Newmusicisstillbeingproduced.Knowingallthenotesdoesnottellyouthebestwaytoputthemtogether.I'vesaidthattherearenosecretswitchestoturnonadvancedfeaturesinPerl,andthismeansthateveryonestartsonalevelplayingfield,injustthesamewaythatJohannSebastianBachandalittlekidplayingwithaxylophonehavepreciselythesamerawmaterialstoworkwith.ThekeytoproducingadvancedPerloradvancedmusicdependsontwothings:knowledgeoftechniquesandexperienceofwhatworksandwhatdoesn't.
Theaimofthisbookistogiveyousomeofeachofthesethings.Ofcourse,nobookcanimpartexperience.Experienceissomethingthatmustbe,well,experienced.However,abooklikethiscanshowyousomeexistingsolutionsfromexperiencedPerlprogrammersandhowtousethemtosolvetheproblems
-
youmaybefacing.
Ontheotherhand,abookcancertainlyteachtechniques,andinthischapterwe'regoingtolookatthethreemajorclassesofadvancedprogrammingtechniquesinPerl.First,we'lllookatintrospection:programslookingatprograms,figuringouthowtheywork,andchangingthem.ForPerlthisinvolvesmanipulatingthesymboltableespeciallyatruntime,playingwiththebehaviorofbuilt-infunctionsandusingAUTOLOADtointroducenewsubroutinesandcontrolbehaviorofsubroutinedispatchdynamically.We'llalsobrieflylookatbytecodeintrospection,whichistheabilitytoinspectsomeofthepropertiesofthePerlbytecodetreetodeterminepropertiesoftheprogram.
Thesecondideawe'lllookatistheclassmodel.Writingobject-orientedprogramsandmodulesissometimesregardedasadvancedPerl,butIwouldcategorizeitasintermediate.Asthisisanadvancedbook,we'regoingtolearnhowtosubvertPerl'sobject-orientedmodeltosuitourgoals.
Finally,there'sthetechniqueofwhatIcallunexpectedcodecodethatrunsinplacesyoumightnotexpectitto.Thismeansrunningcodeinplaceofoperatorsinthecaseofoverloading,someadvancedusesoftying,andcontrollingwhencoderunsusingnamedblocksandeval.
Thesethreeareas,togetherwiththespecialcaseofPerlXSprogrammingwhichwe'lllookatinChapter9onInlinedelineatethefundamentaltechniquesfromwhichalladvancedusesofPerlaremadeup.
-
1.1.Introspection
First,though,introspection.Theseintrospectiontechniquesappeartimeandtimeagaininadvancedmodulesthroughoutthebook.Assuch,theycanberegardedasthemostfundamentaloftheadvancedtechniqueseverythingelsewillbuildontheseideas.
1.1.1.PreparatoryWork:FunwithGlobs
GlobsareoneofthemostmisunderstoodpartsofthePerllanguage,butatthesametime,oneofthemostfundamental.Thisisashame,becauseaglobisarelativelysimpleconcept.
WhenyouaccessanyglobalvariableinPerlthatis,anyvariablethathasnotbeendeclaredwithmytheperlinterpreterlooksupthevariablenameinthesymboltable.Fornow,we'llconsiderthesymboltabletobeamappingbetweenavariable'snameandsomestorageforitsvalue,asinFigure1-1.
Notethatwesaythatthesymboltablemapstostorageforthevalue.Introductoryprogrammingtextsshouldtellyouthatavariableisessentiallyaboxinwhichyoucangetandsetavalue.Oncewe'velookedup$a,weknowwheretheboxis,andwecangetandsetthevaluesdirectly.InPerlterms,thesymboltablemapstoareferenceto$a.
Figure1-1.Consultingthesymboltable,take1
-
Youmayhavenoticedthatasymboltableissomethingthatmapsnamestostorage,whichsoundsalotlikeaPerlhash.Infact,you'dbeaheadofthegame,sincethePerlsymboltableisindeedimplementedusinganordinaryPerlhash.Youmayalsohavenoticed,however,thatthereareseveralthingscalledainPerl,including$a,@a,%a,&a,thefilehandlea,andthedirectoryhandlea.
Thisiswheretheglobcomesin.Thesymboltablemapsanamelikeatoaglob,whichisastructureholdingreferencestoallthevariablescalleda,asinFigure1-2.
Figure1-2.Consultingthesymboltable,take2
-
Asyoucansee,variablelook-upisdoneintwostages:first,findingtheappropriateglobinthesymboltable;second,findingtheappropriatepartoftheglob.Thisgivesusareference,andassigningittoavariableorgettingitsvalueisdonethroughthisreference.
1.1.1.1Aliasing
Thisdisconnectbetweenthenamelook-upandthereferencelook-upenablesustoaliastwonamestogether.First,wegetholdoftheirglobsusingthe*namesyntax,andthensimplyassignoneglobtoanother,asinFigure1-3.
Figure1-3.Aliasingviaglobassignment
-
We'veassignedb'ssymboltableentrytopointtoa'sglob.Nowanytimewelookupavariablelike%b,thefirststagelook-uptakesusfromthesymboltabletoa'sglob,andreturnsusareferenceto%a.
ThemostcommonapplicationofthisgeneralideaisintheExportermodule.IfIhaveamodulelikeso:
packageSome::Module;usebase'Exporter';our@EXPORT=qw(useful);
subuseful{42}
thenExporterisresponsibleforgettingtheusefulsubroutine
-
fromtheSome::Modulepackagetothecaller'spackage.Wecouldmockourownexporterusingglobassignments,likethis:
packageSome::Module;subuseful{42}
subimport{nostrict'refs';*{caller()."::useful"}=*useful;}
Rememberthatimportiscalledwhenamoduleisused.Wegetthenameofthecallingpackageusingcallerandconstructthenameoftheglobwe'regoingtoreplaceforinstance,main::useful.Weuseasymbolicreferencetoturntheglob'sname,whichisastring,intotheglobitself.Thisisjustthesameasthesymbolicreferenceinthisfamiliarbutunpleasantpieceofcode:
$answer=42;$variable="answer";
print${$variable};
Ifwewereusingtherecommendedstrictpragma,ourprogramwoulddieimmediatelyandwithgoodreason,sincesymbolicreferencesshouldonlybeusedbypeoplewhoknowwhatthey'redoing.Weusenostrict'refs';totellPerlthatwe'replanningondoinggoodmagicwithsymbolicreferences.
ManyadvancedusesofPerlneedtodosomeofthethingsthatstrictpreventstheuninitiatedfromdoing.AsaninitiatedPerluser,youwilloccasionallyhavetoturnstricturesoff.Thisisn'tsomethingtotakelightly,butdon'tbeafraidofit;strictisausefulservant,butabadmaster,andshouldbetreatedassuch.
-
Nowthatwehavethe*main::usefulglob,wecanassignittopointtothe*usefulglobinthecurrentSome::Modulepackage.Nowallreferencestouseful()inthemainpackagewillresolveto&Some::Module::useful.
Thatisagoodfirstapproximationofanexporter,butweneedtoknowmore.
1.1.1.2Accessingpartsofaglob
Withournaiveimportroutineabove,wealiasedmain::usefulbyassigningoneglobtoanother.However,thishassomeunfortunatesideeffects:
useSome::Module;our$useful="Somehandystring";
print$Some::Module::useful;
Sincewe'vealiasedtwoentireglobstogether,anychangestoanyofthevariablesintheusefulglobwillbereflectedintheotherpackage.IfSome::Modulehasamoresubstantialroutinethatusesitsown$useful,thenallhellwillbreakloose.
Allwewanttodoistoputasubroutineintothe&usefulelementofthe*main::usefulglob.Ifwewereexportingascalaroranarray,wecouldassignacopyofitsvaluetotheglobbysaying:
${caller()."::useful"}=$useful;@{caller()."::useful"}=@useful;
-
However,ifwetrytosay:
&{caller()."::useful"}=&useful;
theneverythinggoeswrong.The&usefulontherightcallstheusefulsubroutineandreturnsthevalue42,andtherestofthelinewantstocallacurrentlynon-existantsubroutineandassignitsreturnvaluethenumber42.Thisisn'tgoingtowork.
Thankfully,Perlprovidesuswithawayaroundthis.Wedon'thavetoassigntheentireglobatonce.Wejustassignareferencetotheglob,andPerlworksoutwhattypeofreferenceitisandstoresitintheappropriatepart,asinFigure1-4.
Figure1-4.Assigningtoaglob'sarraypart
-
Noticethatthisisnotthesameas@a=@b;itisrealaliasing.Anychangesto@bwillbeseenin@a,andviceversa:
@b=(1,2,3,4);*a=\@b;
push@b,5;print@a;#12345
#However:$a="Bye"$b="Hellothere!";print$a;#Bye
-
Althoughthe@aarrayisaliasedbyhavingitsreferenceconnectedtothereferenceusedtolocatethe@barray,therestofthe*aglobisuntouched;changesin$bdonotaffect$a.
Youcanwritetoallpartsofaglob,justbyprovidingtheappropriatereferences:
*a=\"Hello";*a=[1,2,3];*a={red=>"rouge",blue=>"bleu"};
print$a;#Helloprint$a[1];#2print$a{"red"};#rouge
Thethreeassignmentsmaylookliketheyarereplacingeachother,buteachwritestoadifferentpartoftheglobdependingontheappropriatereferencetype.Iftheassignedvalueisareferencetoaconstant,thenthevariable'svalueisunchangeable.
*a=\1234;$a=10;#Modificationofaread-onlyvalueattempted
Nowwecometoasolutiontoourexporterproblem;wewanttoalias&main::usefuland&Some::Module::useful,butnootherpartsoftheusefulglob.Wedothisbyassigningareferenceto&Some::Module::usefulto*main::useful:
subuseful{42}subimport{
-
nostrict'refs';*{caller()."::useful"}=\&useful;}
ThisissimilartohowtheExportermoduleworks;theheartofExporteristhissegmentofcodeinExporter::Heavy::heavy_export:
foreach$sym(@imports){#shortcutforthecommoncaseofnotypecharacter(*{"${callpkg}::$sym"}=\&{"${pkg}::$sym"},next)unless$sym=~s/^(\W)//;
$type=$1;*{"${callpkg}::$sym"}=$typeeq'&'?\&{"${pkg}::$sym"}:$typeeq'$'?\${"${pkg}::$sym"}:$typeeq'@'?\@{"${pkg}::$sym"}:$typeeq'%'?\%{"${pkg}::$sym"}:$typeeq'*'?*{"${pkg}::$sym"}:do{requireCarp;Carp::croak("Can'texportsymbol:$type$sym")};}
Thishasalistofimports,whichhaveeithercomefromtheuseSome::Module'...';declarationorfromSome::Module'[email protected],ortheymaynot;iftheydonot,suchaswhenyousayuseCarp'croak';,thentheyrefertosubroutines.
Inouroriginalcase,wehadset@EXPORTto("useful").First,Exporterchecksforatypesigilandremovesit:
-
(*{"${callpkg}::$sym"}=\&{"${pkg}::$sym"},next)unless$sym=~s/^(\W)//;
Because$symis"useful"withnotypesigiltherestofthestatementexecuteswitharesultsimilarto:
*{"${callpkg}::$sym"}=\&{"${pkg}::$sym"};next;
Pluggingintheappropriatevalues,thisisverymuchlikeourmockexporter:
*{$callpkg."::useful"}=\&{"Some::Module::useful"};
Ontheotherhand,wherethereisatypesigiltheexporterconstructsthereferenceandassignstherelevantpartoftheglob:
*{"${callpkg}::$sym"}=$typeeq'&'?\&{"${pkg}::$sym"}:$typeeq'$'?\${"${pkg}::$sym"}:$typeeq'@'?\@{"${pkg}::$sym"}:$typeeq'%'?\%{"${pkg}::$sym"}:$typeeq'*'?*{"${pkg}::$sym"}:do{requireCarp;Carp::croak("Can'texportsymbol:$type$sym")};
-
AccessingGlobElements
The*glob=...syntaxobviouslyonlyworksforassigningreferencestotheappropriatepartoftheglob.Ifyouwanttoaccesstheindividualreferences,youcantreattheglobitselfasaveryrestrictedhash:*a{ARRAY}isthesameas\@a,and*a{SCALAR}isthesameas\$a.TheothermagicnamesyoucanuseareHASH,IO,CODE,FORMAT,andGLOB,forthereferencetotheglobitself.TherearealsothereallytrickyPACKAGEandNAMEelements,whichtellyouwheretheglobcamefrom.
Thesedays,accessingglobsbyhashkeysisreallyonlyusefulforretrievingtheIOelement.However,we'llseeanexamplelaterofhowitcanbeusedtoworkwithglobreferencesratherthanglobsdirectly.
1.1.1.3Creatingsubroutineswithglobassignment
OnecommonuseofthealiasingtechniqueinadvancedPerlistheassignmentofanonymoussubroutinereferences,andespeciallyclosures,toaglob.Forinstance,there'samodulecalledData::BT::PhoneBillthatretrievesdatafromBritishTelecom'sonlinephonebillservice.Themoduletakescomma-separatedlinesofinformationaboutacallandturnsthemintoobjects.Anolderversionofthemodulesplitthelineintoanarrayandblessedthearrayasanobject,providingabunchofread-onlyaccessorsfordataaboutacall:
packageData::BT::PhoneBill::_Call;subnew{my($class,@data)=@_;bless\@data,$class;}
subinstallation{shift->[0]}subline{shift->[1]}...
-
Closures
Aclosureisacodeblockthatcapturestheenvironmentwhereit'sdefinedspecifically,anylexicalvariablestheblockusesthatweredefinedinanouterscope.Thefollowingexampledelimitsalexicalscope,definesalexicalvariable$seqwithinthescope,thendefinesasubroutinesequencethatusesthelexicalvariable.
{my$seq=3;subsequence{$seq+=3}}
print$seq;#outofscope
printsequence;#prints6printsequence;#prints9
Printing$seqaftertheblockdoesn'twork,becausethelexicalvariableisoutofscope(it'llgiveyouanerrorunderusestrict.However,thesequencesubroutinecanstillaccessthevariabletoincrementandreturnitsvalue,becausetheclosure{$seq+=3}capturedthelexicalvariable$seq.
Seeperlfaq7andperlrefformoredetailsonclosures.
Ofcourse,theinevitablehappened:BTaddedanewcolumnatthebeginning,andalloftheaccessorshadtoshiftdown:
subtype{shift->[0]}subinstallation{shift->[1]}subline{shift->[2]}
Clearlythiswasn'taseasytomaintainasitshouldbe.Thefirststepwastorewritetheconstructortouseahashinsteadofanarrayasthebasisfortheobject:
-
our@fields=qw(typeinstallationlinechargecard_datetimedestination_number_durationrebate_cost);
subnew{my($class,@data)=@_;bless{map{$fields[$_]=>$data[$_]}0..$#fields}=>$class;}
Thiscodemapstypetothefirstelementof@data,installationtothesecond,andsoon.Nowwehavetorewritealltheaccessors:
subtype{shift->{type}}subinstallation{shift->{installation}}subline{shift->{line}}
Thisisanimprovement,butifBTaddsanothercolumncalledfriends_and_family_discount,thenIhavetotypefriends_and_family_discountthreetimes:onceinthe@fieldsarray,onceinthenameofthesubroutine,andonceinthenameofthehashelement.
It'sacardinallawofprogrammingthatyoushouldneverhavetowritethesamethingmorethanonce.Itdoesn'ttakemuchtoautomaticallyconstructalltheaccessorsfromthe@fieldsarray:
formy$f(@fields){nostrict'refs';*$f=sub{shift->{$f}};}
-
Thiscreatesanewsubroutineintheglobforeachofthefieldsinthearrayequivalentto*type=sub{shift->{type}}.Becausewe'reusingaclosureon$f,eachaccessor"remembers"whichfieldit'stheaccessorfor,eventhoughthe$fvariableisoutofscopeoncetheloopiscomplete.
CreatinganewsubroutinebyassigningaclosuretoaglobisaparticularlycommontrickinadvancedPerlusage.
1.1.2.AUTOLOAD
Thereis,ofcourse,asimplerwaytoachievetheaccessortrick.Insteadofdefiningeachaccessorindividually,wecandefineasingleroutinethatexecutesonanycalltoanundefinedsubroutine.InPerl,thistakestheformoftheAUTOLOADsubroutineanordinarysubroutinewiththemagicnameAUTOLOAD:
subAUTOLOAD{print"Idon'tknowwhatyouwantmetodo!\n";}
yow();
InsteadofdyingwithUndefinedsubroutine&yowcalled,PerltriestheAUTOLOADsubroutineandcallsthatinstead.
TomakethisusefulintheData::BT::PhoneBillcase,weneedtoknowwhichsubroutinewasactuallycalled.Thankfully,Perlmakesthisinformationavailabletousthroughthe$AUTOLOADvariable:
-
subAUTOLOAD{my$self=shift;if($AUTOLOAD=~/.*::(.*)/){$self->{$1}}
Themiddlelinehereisacommontrickforturningafullyqualifiedvariablenameintoalocallyqualifiedname.Acallto$call->typewillset$AUTOLOADtoData::BT::PhoneBill::_Call::type.Sincewewanteverythingafterthelast::,weusearegularexpressiontoextracttherelevantpart.Thiscanthenbeusedasthenameofahashelement.
WemaywanttohelpPerloutalittleandcreatethesubroutineontheflysoitdoesn'tneedtouseAUTOLOADthenexttimetypeiscalled.Wecandothisbyassigningaclosuretoaglobasbefore:
subAUTOLOAD{if($AUTOLOAD=~/.*::(.*)/){my$element=$1;*$AUTOLOAD=sub{shift->{$element}};goto&$AUTOLOAD;}
Thistime,wewriteintothesymboltable,constructinganewsubroutinewherePerlexpectedtofindouraccessorinthefirstplace.Byusingaclosureon$element,weensurethateachaccessorpointstotherighthashelement.Finally,oncethenewsubroutineissetup,wecanusegoto&subnametotryagain,callingthenewlycreatedData::BT::PhoneBill::_Call::typemethodwiththesameparametersasbefore.Thenexttimethesamesubroutineiscalled,itwillbefoundinthesymboltablesincewe'vejustcreateditandwewon'tgothroughAUTOLOADagain.
-
gotoLABELandgoto&subnamearetwocompletelydifferentoperations,unfortunatelywiththesamename.Thefirstisgenerallydiscouraged,butthesecondhasnosuchstigmaattachedtoit.Itisidenticaltosubname(@_)butwithoneimportantdifference:thecurrentstackframeisobliteratedandreplacedwiththenewsubroutine.Ifwehadused$AUTOLOAD->(@_)inourexample,andsomeonehadtoldadebuggertosetabreakpointinsideData::BT::PhoneBill::_Call::type,theywouldseethisbacktrace:
.=Data::BT::PhoneBill::_Call::type....=Data::BT::PhoneBill::_Call::AUTOLOAD....=main::process_call
Inotherwords,we'veexposedtheplumbing,ifonlyforthefirstcalltotype.Ifweusegoto&$AUTOLOAD,however,theAUTOLOADstackframeisobliteratedandreplaceddirectlybythetypeframe:
.=Data::BT::PhoneBill::_Call::type....=main::process_call
It'salsoconcievablethat,becausethereisnothirdstackframeorcall-returnlinkagetohandle,thegototechniqueismarginallymoreefficient.
TherearetwothingsthateveryuserofAUTOLOADneedstoknow.ThefirstisDESTROY.IfyourAUTOLOADsubroutinedoesanythingmagical,youneedtomakesurethatitcheckstoseeifit'sbeingcalledinplaceofanobject'sDESTROYclean-upmethod.Onecommonidiomtodothisisreturnif$1eq"DESTROY".AnotheristodefineanemptyDESTROYmethodintheclass:subDESTROY{}.
ThesecondimportantthingaboutAUTOLOADisthatyoucanneitherdeclinenorchainAUTOLOADs.IfanAUTOLOADsubroutinehasbeencalled,thenthemissingsubroutinehasbeendeemedtobedealtwith.Ifyouwanttorethrowtheundefined-subroutineerror,youmustdosomanually.Forinstance,let'slimitourData::BT::PhoneBill::_Call::AUTOLOADmethodtoonlydealwithrealelementsofthehash,andnotany
-
randomrubbishortypothatcomesourway:
useCarpqw(croak);...subAUTOLOAD{my$self=shift;if($AUTOLOAD=~/.*::(.*)/andexists$self->{$1}){return$self->{$1}}croak"Undefinedsubroutine&$AUTOLOADcalled";}
1.1.3.COREandCORE::GLOBAL
TwoofthemostmisunderstoodpiecesofPerlarcanaaretheCOREandCORE::GLOBALpackages.Thesetwopackageshavetodowiththereplacementofbuilt-infunctions.Youcanoverrideabuilt-inbyimportingthenewfunctionintothecaller'snamespace,butitisnotassimpleasdefininganewfunction.
Forinstance,tooverridetheglobfunctioninthecurrentpackagewithoneusingregularexpressionsyntax,weeitherhavetowriteamoduleorusethesubspragmatodeclarethatwewillbeusingourownversionoftheglobtypeglob:
usesubsqw(glob);
subglob{my$pattern=shift;local*DIR;opendirDIR,"."ordie$!;returngrep/$pattern/,readdirDIR;}
-
ThisreplacesPerl'sbuilt-inglobfunctionforthedurationofthepackage:
print"$_\n"forglob("^c.*\\.xml");
ch01.xmlch02.xml...
However,sincethesyntaxforthegloboperatorisinternallyresolvedtoacalltoglob,wecouldjustaswellsay:
print"$_\n"for;
Neitherofthesewouldworkwithouttheusesubsline,whichpreparesthePerlparserforseeingaprivateversionoftheglobfunction.
Ifyou'rewritingamodulethatprovidesthisfunctionality,alliswellandgood.Justputthenameofthebuilt-infunctionin@EXPORT,andtheExporterwilldotherest.
WheredoCORE::andCORE::GLOBAL::comein,then?First,ifwe'reinapackagethathasanoverridenglobandweneedtogetatPerl'scoreglob,wecanuseCORE::glob()todoso:
@files=;#Newregexpglob@files=CORE::glob("ch*xml");#Oldshell-styleglob
-
CORE::alwaysreferstothebuilt-infunctions.Isay"refersto"asausefulfictionCORE::merelyqualifiestothePerlparserwhichglobyoumean.Perl'sbuilt-infunctionsdon'treallyliveinthesymboltable;they'renotsubroutines,andyoucan'ttakereferencestothem.TherecanbeapackagecalledCORE,andyoucanhappilysaythingslike$CORE::a=1.ButCORE::followedbyafunctionnameisspecial.
Becauseofthis,wecanrewriteourregexp-globfunctionlikeso:
packageRegexp::Glob;usebase'Exporter';our@EXPORT=qw(glob);
subglob{my$pattern=shift;returngrep/$pattern/,CORE::glob("*");}1;
There'saslightproblemwiththis.Importingasubroutineintoapackageonlyaffectsthepackageinquestion.Anyotherpackagesintheprogramwillstillcallthebuilt-inglob:
useRegexp::Glob;@files=glob("ch.*xml");#Newregexpglob
packageElsewhere;@files=glob("ch.*xml");#Oldshell-styleglob
Ourothermagicpackage,CORE::GLOBAL::,takescareofthisproblem.Bywritingasubroutinereferenceinto
-
CORE::GLOBAL::glob,wecanreplacetheglobfunctionthroughoutthewholeprogram:
packageRegexp::Glob;
*CORE::GLOBAL::glob=sub{my$pattern=shift;local*DIR;opendirDIR,"."ordie$!;returngrep/$pattern/,readdirDIR;};
1;
Nowitdoesn'tmatterifwechangepackagesthegloboperatoranditsaliaswillbeourmodifiedversion.
Sothereyouhaveit:CORE::isapseudo-packageusedonlytounambiguouslyrefertothebuilt-inversionofafunction.CORE::GLOBAL::isarealpackageinwhichyoucanputreplacementsforthebuilt-inversionofafunctionacrossallnamespaces.
1.1.4.CaseStudy:Hook::LexWrap
Hook::LexWrapisamodulethatallowsyoutoaddwrappersaroundsubroutinesthatis,toaddcodetoexecutebeforeorafterawrappedroutine.Forinstance,here'saverysimpleuseofLexWrapfordebuggingpurposes:
wrap'my_routine',pre=>sub{print"Abouttorunmy_routinewitharguments@_"},post=>sub{print"Donewithmy_routine";}
-
ThemainsellingpointofHook::LexWrapissummarizedinthemodule'sdocumentation:
Unlikeothermodulesthatprovidethiscapacity(e.g.Hook::PreAndPostandHook::WrapSub),Hook::LexWrapimplementswrappersinsuchawaythatthestandard"caller"functionworkscorrectlywithinthewrappedsubroutine.
It'seasyenoughtofoolcallerifyouonlyhavepre-hooks;youreplacethesubroutineinquestionwithanintermediateroutinethatdoesthemoralequivalentof:
submy_routine{call_pre_hook();goto&Real::my_routine;}
Aswesawabove,thegoto&subnameformobliteratesmy_routine'sstackframe,soitlookstotheoutsideworldasthoughmy_routinehasbeencontrolleddirectly.
Butwithpost-hooksit'sabitmoredifficult;youcan'tusethegoto&trick.Afterthesubroutineiscalled,youwanttogoontodosomethingelse,butyou'veobliteratedthesubroutinethatwasgoingtocallthepost-hook.
SohowdoesHook::LexWrapensurethatthestandardcallerfunctionworks?Well,itdoesn't;itactuallyprovidesitsown,makingsureyoudon'tusethestandardcallerfunctionatall.
Hook::LexWrapdoesitsworkintwoparts.Thefirstpartassignsaclosuretothesubroutine'sglob,replacingitwithanimposterthatarrangesforthehookstobecalled,andthesecond
-
providesacustomCORE::GLOBAL::caller.Let'sfirstlookatthecustomcaller:
*CORE::GLOBAL::caller=sub{my($height)=($_[0]||0);my$i=1;my$name_cache;while(1){my@caller=CORE::caller($i++)orreturn;$caller[3]=$name_cacheif$name_cache;$name_cache=$caller[0]eq'Hook::LexWrap'?$caller[3]:'';nextif$name_cache||$height--!=0;returnwantarray?@_?@caller:@caller[0..2]:$caller[0];}};
Thebasicideaofthisisthatwewanttoemulatecaller,butifweseeacallintheHook::LexWrapnamespace,thenweignoreitandmoveontothenextstackframe.Sowefirstworkoutthenumberofframestobackupthestack,defaultingtozero.However,sinceCORE::GLOBAL::calleritselfcountsasastackframe,weneedtostartthecountinginternallyfromone.
Next,wedoaslightbitoftrickery.OurimpostersubroutineiscompiledintheHook::LexWrapnamespace,butithasthenameoftheoriginalsubroutineit'semulating.SoifweseesomethinginHook::LexWrap,westoreitssubroutinenameawayin$name_cacheandthenskipoverit,withoutdecrementing$height.IfthethingweseeisnotinHook::LexWrap,butcomesdirectlyaftersomethingthatis,wereplaceitssubroutinenamewiththeonefromthecache.Finally,once$heightgetsdowntozero,wecanreturntheappropriatebitsofthe@callerarray.
Bydoingthis,we'vecreatedourownreplacementcaller
-
function,whichhidestheexistenceofstackframesintheHook::LexWrappackage,butinallotherwaysbehavesthesameastheoriginalcaller.Nowlet'sseehowourimpostersubroutineisbuiltup.
Mostofthewraproutineisactuallyjustaboutargumentchecking,contextpropagation,andreturnvaluehandling;wecanslimitdowntothefollowingforourpurposes:
subwrap(*@){my($typeglob,%wrapper)=@_;$typeglob=(ref$typeglob||$typeglob=~/::/)?$typeglob:caller()."::$typeglob";my$original=ref$typeglobeq'CODE'?$typeglob:*$typeglob{CODE};$imposter=sub{$wrapper{pre}->(@_)if$wrapper{pre};my@return=&$original;$wrapper{post}->(@_)if$wrapper{post};return@return;};*{$typeglob}=$imposter;}
Tomakeourimposterwork,weneedtoknowtwothings:thecodewe'regoingtorunandwhereit'sgoingtoliveinthesymboltable.Wemighthavebeeneitherhandedatypeglob(thetrickycase)orthenameofasubroutineasastring.Ifwehaveastring,thecodelookslikethis:
$typeglob=$typeglob=~/::/?$typeglob:caller()."::$typeglob";my$original=*$typeglob{CODE};
-
Thefirstlineensuresthatthenowbadlynamed$typeglobisfullyqualified;ifnot,it'sprefixedwiththecallingpackage.Thesecondlineturnsthestringintoasubroutinereferenceusingtheglobreferencesyntax.
Inthecasewherewe'rehandedagloblike*to_wrap,wehavetousesomemagic.Thewrapsubroutinehastheprototype(*$);hereiswhattheperlsubdocumentationhastosayabout*prototypes:
A"*"allowsthesubroutinetoacceptabareword,constant,scalarexpression,typeglob,orreferencetoatypeglobinthatslot.Thevaluewillbeavailabletothesubroutineeitherasasimplescalaror(inthelattertwocases)asareferencetothetypeglob.
Soif$typeglobturnsouttobeatypeglob,it'sconvertedintoaglobreference,whichallowsustousethesamesyntaxtowriteintothecodepartoftheglob.
The$imposterclosureissimpleenoughitcallsthepre-hook,thentheoriginalsubroutine,thenthepost-hook.Weknowwhereitshouldgointhesymboltable,andsoweredefinetheoriginalsubroutinewithournewone.
Sothisrelativelycomplexmodulereliespurelyontwotricksthatwehavealreadyexamined:first,globallyoverridingabuilt-infunctionusingCORE::GLOBAL::,andsecond,savingawayasubroutinereferenceandthenglobassigninganewsubroutinethatwrapsaroundtheoriginal.
1.1.5.IntrospectionwithB
There'sonefinalcategoryofintrospectionasappliedtoPerl
-
programs:inspectingtheunderlyingbytecodeoftheprogramitself.
Whentheperlinterpreterishandedsomecode,ittranslatesitintoaninternalcode,similartootherbytecode-compiledlanguagessuchasJava.However,inthecaseofPerl,eachoperationisrepresentedasthenodeonatree,andtheargumentstoeachoperationarethatnode'schildren.
Forinstance,fromtheveryshortsubroutine:
subsum_input{my$a=;print$a+1;}
PerlproducesthetreeinFigure1-5.
Figure1-5.Bytecodetree
-
TheBmoduleprovidesfunctionsthatexposethenodesofthistreeasobjectsinPerlitself.Youcanexamineandinsomecasesmodifytheparsedrepresentationofarunningprogram.
Thereareseveralobviousapplicationsforthis.Forinstance,ifyoucanserializethedatainthetreetodisk,andfindawaytoloaditupagain,youcanstoreaPerlprogramasbytecode.TheB::BytecodeandByteLoadermodulesdojustthis.
ThosethinkingthattheycanusethistodistributePerlcodeinanobfuscatedbinaryformatneedtoreadontooursecondapplication:youcanusethetreetoreconstructtheoriginalPerlcode(orsomethingquitelikeit)fromthebytecode,byessentiallyperformingthecompilationstageinreverse.TheB::Deparsemoduledoesthis,anditcantellusalotabouthowPerlunderstandsdifferentcode:
%perl-MO=Deparse-n-e'/^#/||print'
LINE:while(defined($_=)){print$_unless/^#/;}
Thisshowsuswhat'sreallygoingonwhenthe-nflagisused,theinferred$_inprint,andthelogicalequivalenceofX||YandYunlessX.[*](Incidentally,theOmoduleisadriverthatallowsspecifiedB::*modulestodowhattheywanttotheparsedsourcecode.)
[*]The-MO=DeparseflagisequivalenttouseOqw(Deparse);.
Tounderstandhowthesemodulesdotheirwork,youneedtoknowalittleaboutthePerlvirtualmachine.LikealmostallVM
-
technologies,Perl5isasoftwareCPUthatexecutesastreamofinstructions.Manyoftheseoperationswillinvolveputtingvaluesonortakingthemoffastack;unlikearealCPU,whichusesregisterstostoreintermediateresults,mostsoftwareCPUsuseastackmodel.
Perlcodeenterstheperlinterpreter,getstranslatedintothesyntaxtreestructurewesawbefore,andisoptimized.Partoftheoptimizationprocessinvolvesdeterminingaroutethroughthetreebyjoiningtheopstogetherinalinkedlist.InFigure1-6,therouteisshownasadottedline.
Figure1-6.Optimizedbytecodetree
Eachnodeonthetreerepresentsanoperationtobedone:weneedtoenteranewlexicalscope(thefile);setupinternaldatastructuresforanewstatement,suchassettingthelinenumberforerrorreporting;findwhere$alivesandputthatonthestack;findwhatfilehandlerefersto;readalinefromthat
-
filehandleandputthatonthestack;assignthetopvalueonthestack(theresult)tothenextvaluedown(thevariablestorage);andsoon.
Thereareseveraldifferentkindsofoperators,classifiedbyhowtheymanipulatethestack.Forinstance,therearethebinaryoperatorssuchasaddwhichtaketwovaluesoffthestackandreturnanewvalue.readlineisaunaryoperator;ittakesafilehandlefromthestackandputsavaluebackon.Listoperatorslikeprinttakeanumberofvaluesoffthestack,andthenullarypushmarkoperatorisresponsibleforputtingaspecialmarkvalueonthestacktotellprintwheretostop.
TheBmodulerepresentsallthesedifferentkindsofoperatorsassubclassesoftheB::OPclass,andtheseclassescontainmethodsallowingustogetthenextmoduleintheexecutionorder,thechildrenofanoperator,andsoon.
SimilarclassesexisttorepresentPerlscalar,array,hash,filehandle,andothervalues.WecanconvertanyreferencetoaB::objectusingthesvref_2objectfunction:
useB;
my$subref=sub{my$a=;print$a+1;};
my$b=B::svref_2object($subref);#B::CVobject
ThisB::CVobjectrepresentsthesubroutinereferencethatPerlcan,forinstance,storeinthesymboltable.Tolookattheoptreeinsidethisobject,wecalltheSTARTmethodtogetthefirstnodeinthelinkedlistofthetree'sexecutionorder,ortheROOTmethodtofindtherootofthetree.
-
Dependingonwhichopwehave,therearetwowaystonavigatetheoptree.Towalkthetreeinexecutionorder,youcanjustfollowthechainofnextpointers:
my$op=$b->START;
do{printB::class($op).":".$op->name."(".$op->desc.")\n";}while$op=$op->nextandnot$op->isa("B::NULL");
TheclasssubroutinejustconvertsbetweenaPerlclassnamelikeB::COPandtheunderlyingCequivalent,COP;thenamemethodreturnsthehuman-readablenameoftheoperation,anddescgivesitsdescriptionasitwouldappearinanerrormessage.Weneedtocheckthattheopisn'taB::NULL,becausethenextpointerofthefinalopwillbeaCnullpointer,whichBhandilyconvertstoaPerlobjectwithnomethods.Thisgivesusadumpofthesubroutine'soperationslikeso:
COP:nextstate(nextstatement)OP:padsv(privatevariable)PADOP:gv(globvalue)UNOP:readline()COP:nextstate(nextstatement)OP:pushmark(pushmark)OP:padsv(privatevariable)SVOP:const(constantitem)BINOP:add(addition(+))LISTOP:print(print)UNOP:leavesub(subroutineexit)
Asyoucansee,thisisthenaturalorderfortheoperationsin
-
thesubroutine.Ifyouwanttoexaminethetreeintop-downorder,somethingthatisusefulforcreatingthingslikeB::DeparseoralteringthegeneratedbytecodetreewithtrickslikeoptimizerandB::Generate,thentheeasiestwayistousetheB::Utilsmodule.Thisprovidesanumberofhandyfunctions,includingwalkoptree_simple.Thisallowsyoutosetacallbackandvisiteveryopinatree:
useB::Utilsqw(walkoptree_simple);...my$op=$b->ROOT;
walkoptree_simple($op,sub{$cop=shift;printB::class($cop).":".$cop->name."(".$cop->desc.")\n";});
NotethatthistimewestartfromtheROOTofthetreeinsteadoftheSTART;traversingtheoptreeinthisordergivesusthefollowinglistofoperations:
UNOP:leavesub(subroutineexit)LISTOP:lineseq(linesequence)COP:nextstate(nextstatement)UNOP:null(nulloperation)OP:padsv(privatevariable)UNOP:readline()PADOP:gv(globvalue)COP:nextstate(nextstatement)LISTOP:print(print)...
WorkingwithPerlattheoplevelrequiresagreatdealof
-
practiceandknowledgeofthePerlinternals,butcanleadtoextremelyusefultoolslikeDevel::Cover,anop-levelprofilerandcoverageanalysistool.
-
1.2.MessingwiththeClassModel
Perl'sstyleofobjectorientationisoftenmaligned,butitssheersimplicityallowstheadvancedPerlprogrammertoextendPerl'sbehaviorininterestingandsometimesstartlingways.BecauseallthedetailsofPerl'sOOmodelhappenatruntimeandintheopenusinganordinarypackagevariable(@INC)tohandleinheritance,forinstance,orusingthesymboltablesformethoddispatchwecanfiddlewithalmosteveryaspectofit.
Inthissectionwe'llseesometechniquesspecifictoplayingwiththeclassmodel,butwewillalsoexaminehowtoapplythetechniqueswealreadyknowtodistortPerl'ssenseofOO.
1.2.1.UNIVERSAL
Inalmostallclass-basedOOlanguages,allobjectsderivefromacommonclass,sometimescalledObject.Perldoesn'tquitehavethesameconcept,butthereisasinglehard-wiredclasscalledUNIVERSAL,whichactsasalast-resortclassformethodlookups.Bydefault,UNIVERSALprovidesthreemethods:isa,can,andVERSION.
Wesawisabrieflyinthelastsection;itconsultsaclassorobject's@ISAarrayanddetermineswhetherornotitderivesfromagivenclass:
packageCoffee;our@ISA=qw(Beverage::Hot);
subnew{returnbless{temp=>80},shift}
packageTea;usebase'Beverage::Hot';
-
packageLatte;usebase'Coffee';
packagemain;my$mug=Latte->new;
Tea->isa("Beverage::Hot");#1Tea->isa("Coffee");#0
if($mug->isa("Beverage::Hot")){warn'ContentsMayBeHot';}
isaisahandymethodyoucanuseinmodulestocheckthatyou'vebeenhandedtherightsortofobject.However,sincenoteverythinginPerlisanobject,youmayfindthatjusttestingascalarwithisaisnotenoughtoensurethatyourcodedoesn'tblowup:ifyousay$thing->isa(...)onanunblessedreference,Perlwilldie.
Thepreferred"safetyfirst"approachistowritethetestthisway:
my($self,$thing)=@_;croak"YouneedtogivemeaBeverage::Hotinstance"unlesseval{$thing->isa("Beverage::Hot");};
Thiswillworkevenif$thingisundeforanon-reference.
Checkingisarelationshipsisonewaytoensurethatanobjectwillrespondcorrectlytothemethodsthatyouwanttocallonit,butitisnotnecessarilythebestone.Anotheridea,thatofducktyping,statesthatyoushoulddeterminewhetherornottodealwithanobjectbasedonthemethodsitclaimstorespondto,ratherthanitsinheritance.IfourTeaclassdidnotderivefrom
-
Beverage::Hot,butstillhadtemperature,milk,andsugaraccessorsandbrewanddrinkmethods,wecouldtreatitasifitwereaBeverage::Hot.Inshort,ifitwalkslikeaduckanditquackslikeaduck,wecantreatitlikeaduck.[*]
[*]Ofcourse,oneoftheproblemswithducktypingisthatcheckingthatsomethingcanrespondtoanactiondoesnottellushowitwillrespond.WemightexpectaTReeobjectandaDogtobothhaveabarkmethod,butthatwouldn'tmeanthatwecouldusetheminthesameway.
TheuniversalcanmethodallowsustocheckPerlobjectsduck-style.It'sparticularlyusefulifyouhaveabunchofrelatedclassesthatdon'tallrespondtothesamemethods.Forinstance,lookingbackatourB::OPclasses,binaryoperators,listoperators,andpatternmatchoperatorshavealastaccessortoretrievetheyoungestchild,butnullary,unary,andlogicaloperatorsdon't.Insteadofcheckingwhetherornotwehaveaninstanceoftheappropriateclasses,wecanwritegenericallyapplicablecodebycheckingwhethertheobjectrespondstothelastmethod:
$h{firstaddr}=sprintf("%#x",${$op->first})if$op->can("first");$h{lastaddr}=sprintf("%#x",${$op->last})if$op->can("last");
Anotheradvantageofcanisthatitreturnsthesubroutinereferenceforthemethodonceithasbeenlookedup.We'llseelaterhowtousethistoimplementourownmethoddispatchinthesamewaythatPerlwould.
Finally,VERSIONreturnsthevalueoftheclass's$VERSION.ThisisusedinternallybyPerlwhenyousay:
useSome::Module1.2;
-
WhileI'msurethere'ssomethingcleveryoucandobyprovidingyourownVERSIONmethodandhavingitdomagicwhenPerlcallsit,Ican'tthinkwhatitmightbe.
However,thereisonetrickyoucanplaywithUNIVERSAL:youcanputyourownmethodsinit.Suddenly,everyobjectandeveryclassname(andrememberthatinPerlaclassnameisjustastring)respondstoyournewmethod.
OneparticularlycreativeuseofthisistheUNIVERSAL::requiremodule.Perl'srequirekeywordallowsyoutoloadupmodulesatruntime;however,oneofitsmoreannoyingfeaturesisthatitactsdifferentlybasedonwhetheryougiveitabareclassnameoraquotedstringorscalar.Thatis:
requireSome::Module;
willhappilylookupSome/[email protected],ifyousay:
my$module="Some::Module";require$module;
PerlwilllookforafilecalledSome::Moduleinthecurrentdirectoryandprobablyfail.Thismakesitawkwardtorequiremodulesbynameprogramatically.Youhavetoendupdoingsomethinglike:
eval"require$module";
-
whichhasproblemsofitsown.UNIVERSAL::requireisaneatsolutiontothisitprovidesarequiremethod,whichdoestheloadingforyou.Nowyoucansay:
$module->require;
Perlwilltreat$moduleasaclassnameandcalltheclassmethod,whichwillfallthroughtoUNIVERSAL::require,whichloadsupthemodule.
Similarly,theUNIVERSAL::monikermoduleprovidesahuman-friendlynameforanobject'sclass,bylowercasingthetextafterthefinal:::
packageUNIVERSAL;
submoniker{my($self)=@_;my@parts=split/::/,(ref($self)||$self);returnlcpop@parts;}
Thisallowsyoutosaythingslike:
formy$class(@classes){print"Listingofall".$class->plural_moniker.":\n";print$_->name."\n"for$class->retrieve_all;print"\n";}
-
SomepeopledisagreewithputtingmethodsintoUNIVERSAL,buttheworstthatcanhappenisthatanobjectnowunexpectedlyrespondstoamethoditwouldnothavebefore.Andifitwouldnotrespondtoamethodbefore,thenanycalltoitwouldhavebeenafatalerror.Atworst,you'vepreventedtheprogramfrombreakingimmediatelybymakingitdosomethingstrange.Balancingthisagainstthekindofhacksyoucanperpetratewithit,I'dsaythataddingthingstoUNIVERSALisausefultechniqueforthearmoryofanyadvancedPerlhacker.
1.2.2.DynamicMethodResolution
Ifyou'restillconvincedthatPerl'sOOsystemisnotthesortofthingthatyouwant,thenthetimehascometowriteyourown.DamianConway'sObjectOrientedPerlisfullofwaystoconstructnewformsofobjectsandobjectdispatch.
We'veseenthefundamentaltechniquesfordoingthis;it'snowjustamatterofcombiningthem.Forinstance,wecancombineAUTOLOADandUNIVERSALtorespondtoanymethodinanyclassatall.Wecouldusethistoturnallunknownmethodsintoaccessorsandmutators:
subUNIVERSAL::AUTOLOAD{my$self=shift;$UNIVERSAL::AUTOLOAD=~/.*::(.*)/;returnif$1eq"DESTROY";if(@_){$self->{$1}=shift;}$self->{$1};}
Orwecoulduseittomessaboutwithinheritance,like
-
Class::Dynamic;ormakemethodspartofanobject'spayload,likeClass::ClasslessorClass::Object.We'llseelaterhowtoimplementJava-stylefinalattributestopreventmethodsfrombeingoverridenbyderivedclasses.
1.2.3.CaseStudy:SingletonMethods
OntheinfrequentoccasionswhenI'mnotprogramminginPerl,IprograminaninterestinglanguagecalledRuby.RubyisthecreationofJapaneseprogrammerYukihiroMatsumoto,basedonPerlandseveralotherdynamiclanguages.IthasagreatnumberofideasthathaveinfluencedthedesignofPerl6,andsomeofthemhaveevenbeenimplementedinPerl5,aswe'llseehereandlaterinthechapter.
Oneoftheseideasisthesingletonmethod,amethodthatonlyappliestooneparticularobjectandnottotheentireclass.InPerl,theconceptwouldlooksomethinglikethis:
my$a=Some::Class->new;my$b=Some::Class->new;
$a->singleton_method(dump=>sub{my$self=shift;requireData::Dumper;printSTDERRDate::Dumper::Dumper($self)});
$a->dump;#Printsarepresentationoftheobject.$b->dump;#Can'tlocatemethod"dump"
$areceivesanewmethod,but$bdoesnot.Nowthatwehaveanideaofwhatwewanttoachieve,halfthebattleisover.It'sobviousthatinordertomakethiswork,we'regoingtoputa
-
singleton_methodmethodintoUNIVERSAL.Andnowsomehowwe'vegottomake$ahaveallthemethodsthatitcurrentlyhas,butalsohaveanadditionalone.
Ifthismakesyouthinkofsubclassing,you'reontherighttrack.Weneedtosubclass$a(and$aonly)intoanewclassandputthesingletonmethodintothenewclass.Let'stakealookatsomecodetodothis:
packageUNIVERSAL;
subsingleton_method{my($object,$method,$subref)=@_;
my$parent_class=ref$object;my$new_class="_Singletons::".(0+$object);*{$new_class."::".$method}=$subref;
if($new_classne$parent_class){@{$new_class."::ISA"}=($parent_class);bless$object,$new_class;}}
First,wefindwhat$a'soriginalclassis.Thisiseasy,sincereftellsusdirectly.Nextwehavetomakeupanewclassanewpackagenameforoursingletonmethodstolivein.Thishastobespecifictotheobject,soweusetheclosestthingtoauniqueidentifierforobjectsthatPerlhas:thenumericrepresentationofitsmemoryaddress.
-
0+$object
Wedon'ttalkalotaboutmemorylocationsinPerl,sousingsomethinglike0+$objecttofindamemorylocationmaysurpriseyou.However,itshouldbeafamiliarconcept.Ifyou'veeveraccidentallyprintedoutanobjectwhenyouexpectedanormalscalar,youshouldhaveseensomethinglikeSome::Class=HASH(0x801180).ThisisPerl'swayoftellingyouthattheobjectisaSome::Classobject,it'sbasedonahash,anditlivesatthatparticularlocationinmemory.
However,justlikethespecialvariable$!,objectshaveastring/integerduality.Ifyoutreatanobjectasanordinarystring,yougettheoutputwehavejustdescribed.However,ifyoutreatitasanumber,youjustgetthe0x8801180.Bysaying0+$object,we'reforcingtheobjecttoreturnitsmemorylocation,andsincenotwoobjectscanbeatthesamelocation,wehaveapieceofdatauniquetotheobject.
Weinjectthemethodintothenewclasswithglobassignment,andnowweneedtosetupitsinheritancerelationshipon$a'sownclass.SincePerl'sinheritanceishandledbypackagevariables,theseareopenforustofiddlewithdynamically.Finally,wechange$a'sclassbyre-blessingitintothenewclass.
Thefinaltwististhatifthisisthesecondtimetheobjecthashadasingletonmethodaddedtoit,thenitsclasswillalreadybeintheform_Singleton::8393088.Inthiscase,thenewclassnamewouldbethesameastheold,andwereallydon'twanttoalter@ISA,sincethatwouldsetuparecursiverelationship.Perldoesn'tlikethat.
Inonly11linesofcodewe'veextendedthewayPerl'sOOsystemworkswithanewconceptborrowedfromanotherlanguage.Perl'smodelmaynotbeterriblyadvanced,butit'sastonishinglyflexible.
-
1.3.UnexpectedCode
ThefinalsetofadvancedtechniquesinthischaptercoversanythingwherePerlcoderunsatatimethatmightnotbeobvious:tying,forinstance,runscodewhenavariableisaccessedorassignedto;overloadingrunscodewhenvariousoperationsarecalledonavalue;andtimeshiftingallowsustoruncodeoutoforderordelayeduntiltheendofscope.
SomeofthemoststrikingeffectsinPerlcanbeobtainedbyarrangingforcodetoberunatunexpectedmoments,butthismustbetemperedwithcare.Thewholepointofunexpectedcodeisthatit'sunexpected,andthatbreaksthewell-knownPrincipleofLeastSurprise:programmingPerlshouldnotbesurprising.
Ontheotherhand,thesearepowerfultechniques.Let'stakealookathowtomakethebestuseofthem.
1.3.1.Overloading
Overloading,inaPerlcontext,isawayofmakinganobjectlooklikeitisn'tanobject.Morespecifically,it'sawayofmakinganobjectrespondtomethodswhenusedinanoperationorothercontextthatdoesn'tlooklikeamethodcall.
Theproblemwithsuchoverloadingisthatitcanquicklygetwildlyoutofhand.C++overloadstheleftbit-shiftoperator,
-
ontheotherhand,overloadsthesameoperatoronarraystomeanpush.IfwemakeflagrantuseofoverloadinginPerl,weenduphavingtolookatleasttwiceatcodelike:
$object*=$value;
Welookoncetoseeitasamultiplication,oncetorealizeit'sactuallyamethodcall,andoncemoretoworkoutwhatclass$objectisinatthispointandhencewhatmethodhasbeencalled.
Thatsaid,forclassesthatmoreorlessrepresentthesortofthingsyou'reoverloadingnumbers,strings,andsoonthenoverloadingworksfine.Now,howdowedoit?
1.3.1.1Simpleoperatoroverloading
Theclassicexampleofoperatoroverloadingisamodulethatrepresentstime.Indeed,Time::Seconds,fromtheTime::Piecedistributiondoesjustthis.Let'smakesomenewTime::Secondsobjects:
my$min=Time::Seconds->new(60);my$hour=Time::Seconds->new(3600);
ThepointofTime::Secondsisthat,aswellasmerelyrepresentinganumberofseconds,youcanconvertbetweendifferentunitsofduration:
my$longtime=Time::Seconds->new(123456);print$longtime->hours;#34.2933..print$longtime->days;#1.42888..
-
Theseobjectsdefinitelyrepresentanumberanumberofseconds.Normally,we'dhavetoaddthemtogetherwithsomeuglyhacklikethis:
my$new=$min->add($hour);
Andeventhenit'snotclearwhetherornotthatalterstheoriginal$min.Soonenaturaluseofoperatoroverloadingwouldbetoenableustosay$min+$hour,andgetbackanobjectrepresenting3,660seconds.Andthatispreciselywhathappens:
my$new=$min+$hour;print$new->seconds;#3660
ThisisdonebythefollowingbitofcodeintheTime::Secondsmodule:
useoverload'+'=>\&add;#...subadd{my($lhs,$rhs)=_get_ovlvals(@_);returnTime::Seconds->new($lhs+$rhs);}
sub_get_ovlvals{my($lhs,$rhs,$reverse)=@_;$lhs=$lhs->seconds;
if(UNIVERSAL::isa($rhs,'Time::Seconds')){$rhs=$rhs->seconds;}elsif(ref($rhs)){
-
die"Can'tusenonSecondsobjectinoperatoroverload";}
if($reverse){return$rhs,$lhs;}return$lhs,$rhs;}
Theoverloadpragmaisthekeytoitall.IttellsPerltolookmorecarefullyatoperationsinvolvingobjectsofthatclass,anditregistersmethodsforthegivenoperatorsinalook-uptable.Whenanobjectisinvolvedinanoverloadedoperation,theoperationislookedupinthetableandtheresultingmethodcalled.Inthiscase,$obj+$otherwillcall$obj->add($other,0).
ThereasonPerlpassesthreeparameterstothemethodisthatinthecaseof$other+$obj,where$otherisnotanobjectthatoverloads+,westillexpecttheaddmethodtobecalledon$obj.Inthiscase,however,Perlwillcall$obj->add($other,1),tosignifythattheargumentshavebeenreversed.
The_get_ovlvalssubroutinelooksatthetwoargumentstoanoperatorandtriestocoercethemintonumbersotherTime::Secondsobjectsareturnedintonumbersbyhavingthesecondsmethodcalledonthem,ordinarynumbersarepassedthrough,andanyotherkindofobjectcausesafatalerror.Thentheargumentsarereorderedtotheoriginalorder.
Oncewehavetwoordinarynumbers,wecanaddthemtogetherandreturnanewTime::Secondsobjectbasedonthesum.
Theotheroperatorsarebasedonthisprinciple,suchas,whichimplementsallofthecomparisonoperators:
useoverload''=>\&compare;
-
subcompare{my($lhs,$rhs)=_get_ovlvals(@_);return$lhs$rhs;}
Time::Secondsalsooverloadsassignmentoperators+=and-=:
useoverload'-='=>\&subtract_from;subsubtract_from{my$lhs=shift;my$rhs=shift;$rhs=$rhs->secondsifUNIVERSAL::isa($rhs,'Time::Seconds');$$lhs-=$rhs;return$lhs;}
Thisallowsyoutosay$new+=60toaddanotherminutetothenewduration.
Finally,toavoidhavingtowritesuchsubroutinesforeverykindofoperator,Time::Secondsusesafeatureofoverloadcalledfallback.ThisinstructsPerltoattempttoautomaticallygeneratereasonablemethodsfromtheonesspecified:forinstance,the$x++operatorwillbeimplementedintermsof$x+=1,andsoon.Time::Secondssetsfallbacktoundef,whichmeansthatPerlwilltrytouseanautogeneratedmethodbutwilldieifitcannotfindone.
useoverload'fallback'=>'undef';
Alternatevaluesforfallbackincludesometruevalue,whichis
-
themostgeneralfallback:ifitcannotfindanautogeneratedmethod,itwilldowhatitcan,assumingifnecessarythatoverloadingdoesnotexist.Inotherwords,itwillalwaysproducesomevalue,somehow.
Ifyou'reusingoverloadingjusttoaddashortcutoperatorortwoontoanotherwiseobject-basedclassforexample,ifyouwantedtoemulateC++'s(ratherdodgy)useofthe
-
youwouldn'tnormallythinkofasoperators.ThetwomostusefulofthesewehavejustseenwithTime::Secondstheabilitytodictatehowanobjectisconvertedtoastringorintegerwhenusedassuch.
Thisisdonebyassigningmethodstotwospecialoperatornamesthe""operatorforstringificationandthe0+operatorfornumification:
useoverload'0+'=>\&seconds,'""'=>\&seconds;
NowanytimetheTime::Secondsobjectisusedasastringoranumber,thesecondsmethodgetscalled,returningthenumberofsecondsthattheobjectcontains:
print"Onehourplusoneminuteis$newseconds\n";#Onehourplusoneminuteis3660seconds.
Thesearethemostcommonmethodstomakeanoverloadedobjectlookandbehavelikethethingit'smeanttorepresent.Thereareafewothermethodsyoucanplaywithformoreobscureeffects.
Forinstance,youcanoverloadthewaythatanobjectisdereferencedinvariousways,allowingascalarreferencetopretendthatit'salistreferenceorviceversa.TherearefewsensiblereasonstodothisthecuriousObject::MultiTypeoverloadsthe@{},%{},&{},and*{}operatorstoallowasingleobjecttopretendtobeanarray,hash,subroutine,orglob,dependingonhowit'sused.
-
1.3.1.3Non-operatoroverloading
Onelittle-knownextensionoftheoverloadmechanismishiddenawayinthedocumentationforoverload:
ForsomeapplicationPerlparser[sic]manglesconstantstoomuch.Itispossibletohookintothisprocessviaoverload::constant()andoverload::remove_constant()functions.
Thesefunctionstakeahashasanargument.Therecognizedkeysofthishashare
integer
tooverloadintegerconstants,
float
tooverloadfloatingpointconstants,
binary
tooverloadoctalandhexadecimalconstants,
q
tooverload"q"-quotedstrings,constantpiecesof"qq"-and"qx"-quotedstringsandhere-documents,
-
qr
tooverloadconstantpiecesofregularexpressions.
Thatistosay,youcancausethePerlparsertorunasubroutineofyourchoiceeverytimeitcomesacrosssomekindofconstant.Naturally,thisisagainsomethingthatshouldbeusedwithcarebutcanbeusedtosurprisingeffect.
Thesubroutinessuppliedtooverload::constantpassthreeparameters:thefirstistherawformastheparsersawit,thesecondisthedefaultinterpretation,andthethirdisamnemonicforthecontextinwhichtheconstantoccurs.Forinstance,given"camel\nalpaca\npanther",thefirstparameterwouldbecamel\nalpaca\npanther,whereasthesecondwouldbe:
camelalpacapanther
Asthisisadouble-quoted(qq)string,thethirdparameterwouldbeqq.
Forinstance,thehigh-precisionmathlibrariesMath::BigIntandMath::BigFloatprovidetheabilitytoautomaticallycreatehigh-precisionnumbers,byoverloadingtheconstantoperation.
%perl-MMath::BigFloat=:constant-le'printref(123456789012345678901234567890\>1234567890)'Math::BigFloat
-
Thisallowsthelibrariestogetatallthenumbersinaprogram,providinghigh-precisionmathwithouttheexplicitcreationofoverloadedMath::BigFloatobjects.Thecodethatdoesitisstunninglysimple:
subimport{my$self=shift;#...overload::constantfloat=>sub{$self->new(shift);};}
Whentheparserseesafloatingpointnumber(onetoolargetobestoredasaninteger)itpassestherawstringasthefirstparameterofthesubroutinereference.Thisisequivalenttocalling:
Math::BigFloat->new("1234567890123456789012345678901234567890")
atcompiletime.
TheMath::Big*librariescangetawaywiththisbecausetheyarerelativelywellbehaved;thatis,aPerlprogramshouldnotnoticeanydifferenceifallthenumbersaresuddenlyoverloadedMath::BigIntobjects.
Ontheotherhand,here'saslightlymorecrazyuseofoverloading...
I'vealreadymentionedRubyasbeinganotherfavoritelanguageofmine.OneofthedrawsaboutRubyisthatabsolutelyeverythingisanobject:
-
%irbirb(main):001:0>2=>2irb(main):002:0>2.class=>Fixnumirb(main):003:0>2.class.class=>Classirb(main):004:0>2.class.class.class=>Classirb(main):005:0>2.methods=>["=","divmod","+","floor","to_int","to_i","chr","truncate","round","ceil","integer?","prec_f","prec_i","prec","coerce","nonzero?","+@","remainder","eql?","= = =","clone","between?","is_a?","equal?","singleton_methods","freeze","instance_of?","send","methods","tainted?","id","instance_variables","extend","dup","protected_methods","=~","frozen?","kind_of?","respond_to?","class","nil?","instance_eval","public_methods","_ _send_ _","untaint","_ _id_ _","inspect","display","taint","method","private_methods","hash",
-
"to_a"]
Ilikethatyoucancallmethodsona2.Ilikethatyoucandefineyourownmethodstocallona2.Ofcourse,youcan'tdothatinPerl;2isnotanobject.
Butwecanfakeit.Ruby.pmwasaproof-of-conceptmoduleIstartedworkontodemonstratethatyoucandothissortofthinginPerl.Here'swhatitlookslike:
useRuby;print2->class;#"FixInt"print"HelloWorld"->class->class#"Class"print2->class->to_s->class#"String"print2->class->to_s->length#"6"print((2+2)->class)#"FixInt"
#Oreven:print2.class.to_s.class#"String"
Howcanthispossiblywork?Obviously,theonlythingthatwecancallmethodsonareobjects,soconstantslike2andHelloWorldneedtoreturnobjects.Thistellsusweneedtobeoverloadingtheseconstantstoreturnobjects.Wecandothateasilyenough:
packageRuby;subimport{overload::constant(integer=>sub{returnFixnum->new(shift)},q=>sub{returnString->new(shift)},qq=>sub{returnString->new(shift)});
-
}
Wecanmaketheseobjectsblessedscalarreferences:
packageFixnum;subnew{returnbless\$_[1],$_[0]}
packageString;subnew{returnbless\$_[1],$_[0]}
Thisallowsustofilltheclassesupwithmethodsthatcanbecalledontheconstants.That'sagoodstart.Theproblemisthatourconstantsnowbehavelikeobjects,insteadoflikethestringsandnumberstheyrepresent.Wewant"HelloWorld"tolooklikeandactlike"HelloWorld"insteadoflike"String=SCALAR(0x80ba0c)".
Togetaroundthis,weneedtooverloadagainwe'veoverloadedtheconstantstobecomeobjects,andnowweneedtooverloadthoseobjectstolooklikeconstantsagain.Let'slookatthestringclassfirst.Thefirstthingweneedtooverloadisobviouslystringification;whentheobjectisusedasastring,itneedstodisplayitsstringvaluetoPerl,whichwedobydereferencingthereference.
useoverload'""'=>sub{${$_[0]}};
Thiswillgetusmostofthewaythere;wecannowprintoutourStringsandusethemanywherethatanormalPerlstringwouldbeexpected.Next,wetakenoteofthefactthatinRuby,Stringscan'tbecoercedintonumbers.Youcan'tsimplysay2+"10",becausethisisanoperationbetweentwodisparatetypes.
-
TomakethishappeninourStringclass,wehavetooverloadnumification,too:
useCarp;useoverload"0+"=>sub{croak"Stringcan'tbecoercedintoFixnum"};
YoumightlikethefactthatPerlconvertsbetweentypesmagically,butthereasonwhyRubycan'tdoitisbecauseitusesthe+operatorforbothnumericadditionandstringconcatenation,justlikeJavaandPython.Let'soverload+togiveusstringconcatenation:
useoverload"+"=>sub{String->new(${$_[0]}."$_[1]")};
Therearetwothingstonoteaboutthis.ThefirstisthatwehavetobesurethatanyoperationsthatmanipulatestringswillthemselvesreturnStringobjects,orotherwisewewillendupwithordinarystringsthatwecannolongercallmethodson.ThisisnecessaryintheFixnumanaloguetoensurethat(2+2)->classstillworks.Theotherthingisthatwemustexplicitlyforcestringificationontheright-handoperand,forreasonssoontobecomeapparent.
Turningtemporarilytothenumericclass,wecanfillintwooftheoverloadmethodsinthesamesortofway:
useoverload'""'=>sub{croak"failedtoconvertFixnumintoString"},"0+"=>sub{${$_[0]}},
-
However,methodslike+havetobetreatedcarefully.Wemightfirsttrydoingsomethinglikethis:
useoverload'+'=>sub{${$_[0]}+$_[1]};
However,ifwethentry2+"12"thenwegetthebizarreresult122,andfurtherproddingfindsthatthisisaString.Why?
WhathappensisthatPerlfirstseesFixnum+Stringandcallstheoverloadedmethodwe'vejustcreated.Insidethismethod,itconvertstheFixnumobjecttoitsintegervalueandnowhasinteger+String.
Theintegerisnotoverloaded,buttheStringobjectis.IfPerlcanseeanoverloadedoperation,itwilltryandcallit,reorderingtheoperationasString+integer.SinceStringhasanoverloaded+method,too,thatgetscalled,creatinganewstring,whichcatenatestheStringandtheinteger.Oops.
Ideally,wewouldfindawayofconvertingtheright-handsideofthe+operationonaFixnumtoanhonest-to-goodnessnumber.Unfortunately,whilePerlhasanexplicitstringificationoperator,"",whichweusedtoavoidthisproblemintheStringcase,thereisn'tanexplicitnumificationoperator;overloaduses0+asaconvenientmnemonicfornumification,butthisismerelydescribingtheoperationintermsofthe+operator,whichcanbeoverloaded.Sotofixupour+method,wehavetogetalittletechnical:
useoverload'+'=>\∑
subsum{my($left,$right)=@_;my$rval;if(my$numify=overload::Method($right,"0+")){
-
$rval=$right->$numify;}else{$rval=$right;}Fixnum->new($$left+$rval);}
Toexplicitlynumifytheright-handside,weaskoverloadifthatvaluehasanoverloadednumification.Ifitdoes,Methodwillreturnthemethod,andwecancallitandexplicitlynumifythevalueinto$rval.Oncewe'vegottwoplainoldnumbers,weaddthemtogetherandreturnanewnumberoutofthetwo.
Next,weaddoverloadfallback=>1;toeachclass,toprovidedo-what-I-mean(DWIM)methodsfortheoperatorsthatwedon'tdefine.Thisiswhatyouwanttodoforanycasewhereyouwantanobjecttocompletelyemulateastandardbuilt-intype,ratherthanjustaddoneortwooverloadedmethodsontosomethingthat'sessentiallyanobject.
Finally,asalittleflourish,wewanttomakethelastlineofourexamplework:
print2.class.to_s.class#"String"
OneofthereasonsRuby'sconcatenationoperatoris+istofreeup.forthepreferreduseinmostOOlanguages:methodcalls.Thisisn'tveryeasytodoinPerl,butwecanfakeitenoughforariggeddemo.Obviouslywe'regoingtoneedtooverloadtheconcatenationoperator.ThekeytoworkingouthowtomakeitworkistorealizewhatthosethingslikeclassareinaPerlcontext:they'rebarewords,orjustordinarystrings.HenceifweseeaconcatenationbetweenoneofourRubyobjectsandanordinarystring,weshouldcallthemethodwhosenameisin
-
thestring:
useoverload"."=>sub{my($obj,$meth)=@_;$obj->$meth};
Andpresto,wehaveRuby-likeobjectsandRuby-likemethodcalls.Themethodcallmagicisn'tperfectwe'llseelaterhowitcanbeimprovedbuttheRuby-likeobjectscannowrespondtoanymethodswewanttoputintotheirclasses.It'snothardtobuildupafullclasshierarchyjustlikeRuby'sown.
-
Limitations
Ofcourse,ouroverloadingshenanigansdonotmanagetodealwith,forinstance,turningarraysintoobjects.AlthoughPerlisprettyflexible,thatreallycan'tbedonewithoutchangingthewaythemethodcalloperatorworks.
Thatdoesn'tnecessarilystoppeople;thehackerknownonlyas"chocolateboy"hascreatedamodulecalledautobox,whichrequiresapatchtothePerlcore,butwhichallowsyoutotreatanybuilt-inPerldatatypeasanobject.
1.3.2.TimeShifting
ThefinalfundamentaladvancedtechniquewewanttolookatisthatofpostponingorreorderingtheexecutionofPerlcode.Forinstance,wemightwanttowaituntilallmoduleshavebeenloadedbeforemanipulatingthesymboltable,wemightwanttoconstructsomecodeandrunitimmediatelywitheval,orwemightwanttoruncodeattheendofascope.
TherearePerlkeywordsforalloftheseconcepts,andjudicioususeofthemcanbeeffectiveinachievingawidevarietyofeffects.
1.3.2.1Doingthingsnowwitheval/BEGIN
Thebasicinterfacetotime-shiftingisthroughaseriesofnamedblocks.ThesearelikespecialsubroutinesthatPerlstoresinaqueueandrunsatstrategicpointsduringthelifetimeofaprogram.
ABEGINblockisexecutedassoonasPerlcompilesthecode:
-
print"Icomesecond!\n";BEGIN{print"Icomefirst!\n";}
ThesecondlineappearsfirstbecausePerldoesnotordinarilyruncodeasitseesit;itwaitsuntilithascompiledaprogramandallofitsdependenciesintothesortofoptreewesawinoursectiononB,andthenrunsitall.However,BEGINforcesPerltorunthecodeassoonastheindividualblockhasbeencompiledbeforetheofficialruntime.
Infact,theusedirectivetoloadamodulecanbethoughtofas:
BEGIN{requireModule::Name;Module::Name->import(@stuff);}
becauseitcausesthemodule'scodetobeloadedupanditsimportmethodtoberunimmediately.
OneuseoftheimmediateexecutionnatureoftheBEGINblockisintheAnyDBM_Filemodule.ThismoduletriestofindanappropriateDBMmoduletoinheritfrom,meaningthatsolongasoneofthefivesupportedDBMmodulesisavailable,anycodeusingDBMsoughttowork.
Unfortunately,someDBMimplementationsaremorereliablethanothers,oroptimizedfordifferenttypesofapplication,soyoumightwanttospecifyapreferredsearchorderthatisdifferentfromthedefault.Butwhen?AsAnyDBM_Fileloads,itsetsupits@ISAarrayandrequirestheDBMmodules.
ThetrickistouseBEGIN;ifAnyDBM_Fileseesthatsomeoneelsehasputan@ISAarrayintoitsnamespace,itwon'toverwriteitwithitsdefaultone.Sowesay:
-
BEGIN{@AnyDBM_File::ISA=qw(DB_FileGDBM_FileNDBM_File);}useAnyDBM::File;
Thiswouldn'tworkwithouttheBEGIN,sincethestatementwouldthenonlybeexecutedatruntime;wayaftertheusehadsetupAnyDBM_File.
AswellasaBEGIN,there'salsoanENDblock,whichstoresupcodetorunrightattheendoftheprogram,and,infact,thereareaseriesofotherspecialblocksaswell,asshowninFigure1-7.
Figure1-7.Namedblocks
TheCHECKblocksandtheINITblocksareprettymuchindistinguishable,runningjustbeforeandjustafterexecutionbegins.Theonlydifferenceisthatexecutingperlwiththe-cswitch(compilationchecks)willrunCHECKblocksbutnotINITblocks.(Thisalsomeansthatifyouloadamoduleatruntime,itsCHECKandINITblockswon'tberun,becausethetransition
-
betweentheglobalcompilationphaseandtheglobalruntimeexecutionhasalreadypassed.)Let'stakealookatwhatwecandowithaCHECKblock.
1.3.2.2DoingthingslaterwithCHECK
Earlier,wetalkedaboutmessingwithinheritancerelationshipsandstealingideasfromotherlanguages.Let'snowimplementanewmodule,whichgivesustheJavaconceptoffinalmethods.Afinalmethodisonethatcannotbeoverridenbyinheritance:
packageBeverage::Hot;subserve:final{#Ihaveexclusiverightstodefiningthismethod!my($self,$who)=@_;if($who->waitress){$who->waitress->serve($self,$who);}else{$who->take($self);}}
packageTea;usebase'Beverage::Hot';
subserve{#Compile-timeerror.}
We'lldothisbyallowingausertospecifya:finalattributeonamethod.Thisattributewillmarkamethodforlaterchecking.Oncecompiletimehasfinished,we'llcheckoutalltheclassesthatderivefromthemarkedclass,anddiewithanerrorifthederivedclassimplementsthefinalmethod.
-
Attributes
TheideaofattributescameinPerl5.005,withtheattrsmodule.Thiswaspartofthreadingsupportandallowedyoutomarkasubroutineasbeingamethodorbeinglockedforthreadingthatis,itonlyallowsonethreadtoaccessthesubroutineorthemethod'sinvocantatonce.In5.6.0,thesyntaxwaschangedtothenow-familiarsubname:attr,anditalsoalloweduser-definedattributes.
PerhapstheeasiestwaytogetintoattributeprogrammingforanythingtrickyistouseDamianConway'sAttribute::Handlersmodule:thisallowsyoutodefinesubroutinestobecalledwhenanattributeisseen.
Thefirstthingwewanttodoistakeanoteofthoseclassesandmethodsmarkedfinal.WeneedtoswitchtotheUNIVERSALclass,sothatourattributeisvisibleeverywhere.We'llalsouseahash,%marked,togroupthemarkedmethodsbypackage:
packageUNIVERSAL;useAttribute::Handlers;subfinal:ATTR{my($pack,$ref)=@_;push@{$marked{$pack}},*{$ref}{NAME};}
TheAttribute::Handlerspackagearrangesforourhandlertobecalledwithvariousparameters,ofwhichweareonlyinterestedinthefirsttwothepackagethathasthemarkedsubroutineinitandtheglobreferenceforthesubroutineitselfbecausewecangetthesubroutine'snamefromthat.(NAMEisoneofthemagicnameswecanusetoaccessaglob'sslotitreturnsthenameofthesymboltableentry.*{Tea::serve}{NAME}wouldreturnserve.)
Nowwe'vegotourlistofmarkedmethods.Weneedtofinda
-
waytointerruptPerljustbeforeitrunsthescriptbutafterallthemodulesthatweplantousehavebeencompiledandalltheinheritencerelationshipssetup,sothatwecanchecknobodyhasbeennaughtyandoverridenafinalizedmethod.
TheCHECKkeywordgivesusawaytodothis.Itregistersablockofcodetobecalledaftercompilationhasbeenfinishedbutbeforeexecutionbegins.[*]
[*]Incidentally,theOcompilermodulewementionedearlierworksbymeansofCHECKblocksafterallthecodehasbeencompiled,Ohastheselectedcompilerbackendvisittheopcodetreeandspitoutwhateveritwantstodo,thenexitsbeforethecodeisrun.
Toenableustotestthemodule,itturnsoutwewanttohaveourCHECKblockcallanotherfunction.Thisisbecausewecanthenrunthecheckertwice,oncewithoutanoffendingmethodandoncewith:
CHECK{Attribute::Final->check}
Whatwillourcheckingmethoddo,though?Itneedstovisitalltheclassesthatderivefromthoseclasseswehaveinour%markedhash,andtodothat,ithastoknowallthepackagesinthesystem.Sofirstwe'llwritealittlefunctiontorecursivelywalkoverthesymboltable,collectingnamesofpackagesitsees.
Thesymboltableisjustahash,andwecanfindglobnamesbylookingatthekeysofthehash.Tomakematterseveneasier,packagenamesarejusthashkeysthatendin::.Soourcollectorfunctionlookslikethis:
subfill_packages{nostrict'refs';my$root=shift;my@subs=greps/::$//,keys%{$root."::"};push@all_packages,$root;
-
for(@subs){nextif$rooteq"main"and$_eq"main";#Loopfill_packages($root."::".$_);}}
Thenextlineavoidsthepotentialtrapofloopingforever,becausethemain::packagecontainsanentrytoitself.Nowwecanstartlookingatthecheckfunction.Itonlyhastodealwiththosepackagesthathavesomekindofinheritancerelationship,soifapackagedoesnothavean@ISA,thenwecandiscardit:
subcheck{nostrict'refs';fill_packages("main")unless@all_packages;formy$derived_pack(@all_packages){nextunless@{$derived_pack."::ISA"};...}}
Next,wehavealistofmarkedpackagesthatcontainfinalmethods.Wewanttolookspecificallyatcircumstanceswhereaderivedpackagederivesfromamarkedpackage:
formy$derived_pack(@all_packages){nextunless@{$derived_pack."::ISA"};formy$marked_pack(keys%marked){nextunless$derived_pack->isa($marked_pack);...
-
Atthispoint,weknowwehaveasuspectpackage.Ithastherightkindofinheritancerelationship,butdoesitoverridethefinalizedmethod?
formy$meth(@{$marked{$marked_pack}}){my$glob_ref=\*{$derived_pack."::".$meth};if(*{$glob_ref}{CODE}){
Ifthecodeslotispopulated,thenwehaveindeedfoundanaughtymethod.Atthispoint,allthat'slefttodoisreportwhereitcamefrom.WecandothatwiththeBtechnique:byturningtheglobintoaB::GVobject,wegainaccesstotheotherwiseunreachableFILEandLINEmethods,whichtelluswheretheglobentrywasconstructed.
my$name=$marked_pack."::".$meth;my$b=B::svref_2object($glob_ref);die"Cannotoverridefinalmethod$nameat".$b->FILE.",line".$b->LINE."\n";
AndthatistheessenceofworkingwithCHECKblocks:theyallowustodothingswiththesymboltableonceeverythingisinplace,onceallthemoduleshavebeenloaded,andoncetheinheritancerelationshipsandotherfactorshavebeensetup.Ifyoueverfeelyouneedtodosomethinginamodulebutyoudon'twanttodoitquiteyet,puttingitinaCHECKblockmightjustbetherighttechnique.
-
1.3.2.3DoingthingsattheendwithDESTROY
We'vereferredtothespecialDESTROYmethod,whichiscalledwhenanobjectgoesoutofscope.Generallythisisusedforwritingoutstatetodisk,breakingcircularreferences,andotherfinalizationtasks.However,youcanuseDESTROYtoarrangeforthingstobedoneattheendofascope:
subdo_later(&){blessshift,"Do::Later"}subDo::Later::DESTROY{$_[0]->()};
{my$later=do_later{print"Endofblock!\n";};...}
Solongas$latersticksaround,thecodedoesn'tgetcalled.Whenitgoesoutofscope,getsundefined,orthefinalreferencetoitgoesaway,thenthecodeblockiscalled.Hook::LexWrap,oneofthemoduleswelookedatearlierinthechapter,actuallyusesasimilartricktoturnoffthewrappingofasubroutineattheendofalexicalscope:
my$unwrap;$imposter=sub{if($unwrap){goto&$original}...}...returnblesssub{$unwrap=1},'Hook::LexWrap::Cleanup';
-
Whileyoukeepholdofthereturnvaluefromwrap,theimpostercallsthewrappingcode.However,oncethatvaluegoesoutofscope,theclosuresets$unwraptoatruevalue,andfromthenontheimpostersimplyjumpstotheoriginalroutine.
1.3.2.4Casestudy:Acme::Dot
Oneexamplethatputsitalltogethermessingaboutwiththesymboltable,shiftingthetimingofcodeexecution,andoverloadingismyownAcme::Dotmodule.
Ifyou'renotfamiliarwithCPAN'sAcme::*hierarchy,we'llcoveritinmoredetailinChapter10,butfornowyoushouldknowit'sformodulesthatarenotentirelyserious.Acme::Dotisfarfromserious,butitdemonstratesalotofseriousadvancedtechniques.
TheideaofAcme::Dotwastoabstractthe$variable.methodoverloaded.operatorfromRuby.pmandallowthird-partymodulestouseit.Italsogoesalittlefurther,allowing$variable.method(@arguments)towork.And,ofcourse,itdoessowithoutusingsourcefiltersoranyothernon-Perlhackery;thatwouldbecheatingoratleastinelegant.
So,howdowemakethiswork?Weknowthemaintrick,fromRuby.pm,ofoverloadingconcatentationonanobject.However,therearetwoniggles.Thefirstisthatpreviously,where$foo.classwasavariable"concatenated"withaliteralstring,$foo.method(@args)isgoingtobeparsedasasubroutinecall.That'sfine,forthetimebeing;we'llassumethatthereisn'tgoingtobeasubroutinecalledmethodkickingaroundanywherefornow,andlaterwe'llfixupthecasewherethereisone.WewantPerltocalltheundefinedsubroutinemethod,becauseifanundefinedsubroutinegetscalled,wecancatchitwithAUTOLOADandsubvertit.
-
Inwhatwaydoweneedtosubvertit?IntheRuby.pmcase,wesimplyturnedtheright-handsideoftheconcatenation(classin$var.class)andusedthatasamethodname.Inthiscase,weneedtonotonlyknowthemethodname,butthemethod'sparameters,aswell.So,ourAUTOLOADroutinehastoreturnadatastructurethatholdsthemethodnameandtheparameter.Ahashisanaturalwayofdoingthis,althoughanarraywoulddojustaswell:
subAUTOLOAD{$AUTOLOAD=~/.*::(.*)/;returnif$1eq"DESTROY";return{data=>\@_,name=>$1}}
Asusual,wetakecaretoavoidclobberingDESTROY.Nowthatwehavetheargumentsandthename,wecanwriteouroverloadsubroutinetofirethecorrectmethodcallonconcatenation.Ontheleftwillbetheobject,andontherightwillbetheresultofourAUTOLOADroutinethedatastructurethattellsuswhichmethodtofireandwithwhatparameters.
useoverload"."=>sub{my($obj,$stuff)=@_;@_=($obj,@{$stuff->{data}});goto&{$obj->can($stuff->{name})};},fallback=>1;
JustasinRuby,weusethegototricktoavoidupsettinganythingthatreliesoncaller.[*]Nowwehavetheeasypartdone.
[*]Although,tobehonest,Idon'tbelievetherereallyis(oroughttobe)anythingthatreliesonthebehaviorofcalleratleast,nothingthatisn'tdoingadvancedthingsitself.
-
Isaythisistheeasypartbecauseweknowhowtodothisforonepackage.Sofarwe'veglossedoverthefactthatthemethodsandtheoverloadroutinearegoingtoliveinoneclass,andtheAUTOLOADsubroutinehastobepresentwhereverthe$var.methodmethodcallsaregoingtobemade.Tomakemattersworse,ourAcme::Dotmoduleisgoingtobeneitherofthesepackages.We'regoingtoseesomethinglikethis:
packageMy::Class;useAcme::Dot;usebase'Class::Accessor';__PACKAGE__->mk_accessors(qw/nameage/);
packageEnd::User;useMy::Class;
my$x=newMy::Class;$x.name("Winnie-the-Pooh");
It'stheOOclassthatneedstouseAcme::Dotdirectly,anditwillhavetheoverloadroutine.WecantakecareofthiseasilybymakingAcme::Dot'simportmethodsetuptheoverloadinginitscaller:
my($call_pack);
subimport{nostrict'refs';$call_pack=(caller())[0];eval{data}});goto\&{\$obj->can(\$stuff->{name})};
-
},fallback=>1;
EOT;}
However,there'sthethirdpackage,theEnd::Userpackage,whichactuallyneverseesAcme::Dotatall.ItjustusesMy::Classandexpectstogetthedot-operatorfunctionalityaspartofthatclass.Meanwhile,ourpoorAcme::DotclasshastosomehowfindoutwhichclassistheenduserandinstallanAUTOLOADroutineintoit.
Thankfully,weknowthattheend-userclasswillcallMy::Class->import,sowecanuseglobassignmenttomakeMy::Class::importconveysomeinformationbacktoAcme::Dot.WecanmodifyAcme::Dot'simportroutinealittle:
my($call_pack,$end_user);
subimport{nostrict'refs';$call_pack=(caller())[0];*{$call_pack."::import"}=sub{$end_user=(caller())[0];};eval{data}});goto\&{\$obj->can(\$stuff->{name})};},fallback=>1;
EOT;
-
}
Asyoucansee,we'venowglobassignedMy::Class'simportroutineandmadeitsaveawaythenameofthepackagethatusedit:theend-userclass.
Andnow,sinceeverythingissetup,weareatthepointwherewecaninjecttheAUTOLOADintotheenduser'sclass.WeuseaCHECKblocktotime-shiftthistotheendofcompilation:
CHECK{#Atthispoint,everythingisready,and$end_usercontains#thecallingpackage'scallingpackage.nostrict;if($end_user){*{$end_user."::AUTOLOAD"}=sub{$AUTOLOAD=~/.*::(.*)/;returnif$1eq"DESTROY";return{data=>\@_,name=>$1}}}}
AndthatisessentiallyhowAcme::Dotoperates.Itisn'tperfect;ifthere'sasubroutineintheend-userpackagewiththesamenameasamethodontheobject,AUTOLOADwon'tbecalled,andwewillrunintoproblems.It'spossibletoworkaroundthat,bymovingallthesubroutinestoanotherpackage,dispatchingeverythingviaAUTOLOADandusingBtoworkoutwhetherwe'reinthecontextofaconcatenationoperator,but...hey,it'sonlyanAcme::*module.AndIhopeit'smadeitspointalready.
-
1.4.Conclusion
We'venowlookedatmanyoftheadvancedtechniquesusedinpurePerlmodules,mostoftheminvolvinghowtomanipulatethewayPerloperates.We'vedividedthoseroughlyintosectionsonmessingwiththesymboltable,messingwiththeclassmodel,andmakingcoderunwherecodemightnotbeexpected.
Inasense,everythingelseinthisbookwillbebuiltonthetechniquesthatwe'veseenhere.However,Perlisapragmaticlanguage,andinsteadoflookingintheabstractattechniquesthatmightbeuseful,we'regoingtoseehowthesetricksarealreadybeingusedinreal-lifecodeinCPANmodulesandhowtheycanmakeyourprogramminglifeeasier.
-
Chapter2.ParsingTechniquesOnethingPerlisparticularlygoodatisthrowingdataaround.Therearetwotypesofdataintheworld:regular,structureddataandeverythingelse.Thegoodnewsisthatregulardatacolondelimited,tabdelimited,andfixed-widthfilesisreallyeasytoparsewithPerl.Wewon'tdealwiththathere.Thebadnewsisthatregular,structureddataistheminority.
Ifthedataisn'tregular,thenweneedmoreadvancedtechniquestoparseit.Therearetwomajortypesofparserforthiskindoflesspredictabledata.Thefirstisabottom-upparser.Let'ssaywehaveanHTMLpage.Wecansplitthedataupintomeaningfulchunksortokenstagsandthedatabetweentags,forinstanceandthenreconstructwhateachtokenmeans.SeeFigure2-1.Thisapproachiscalledbottom-upparsingbecauseitstartswiththedataandworkstowardaparse.
Figure2-1.Bottom-upparsingofHTML
Theothermajortypeofparserisatop-downparser.ThisstartswithsomeideasofwhatanHTMLfileoughttolooklike:ithasantagatthestartandanattheend,withsomestuffinthemiddle.Theparsercanfindthatpatterninthe
-
documentandthenlooktoseewhatthestuffinthemiddleislikelytobe.SeeFigure2-2.Thisiscalledatop-downparsebecauseitstartswithallthepossibleparsesandworksdownuntilitmatchestheactualcontentsofthedocument.
Figure2-2.Top-downparsingofHTML
-
2.1.Parse::RecDescentGrammars
DamianConway'sParse::RecDescentmoduleisthemostwidelyusedparsergeneratorforPerl.Whilemosttraditionalparsergenerators,suchasyacc,producebottom-upparsers,Parse::RecDescentcreatestop-downparsers.Indeed,asitsnameimplies,itproducesarecursivedescentparser.Oneofthebenefitsoftop-downparsingisthatyoudon'tusuallyhavetosplitthedataintotokensbeforeparsing,whichmakesiteasierandmoreintuitivetouse.
2.1.1.SimpleParsingwithParse::RecDescent
I'macompulsiveplayeroftheJapanesegameofGo.[*]WegenerallyuseafileformatcalledSmartGameFormat(http://www.red-bean.com/sgf/)forexchanginginformationaboutGogames.Here'sanexampleofanSGFfile:
[*]TheAmericanGoAssociationprovidesanintroductiontoGobyKarlBakercalledTheWaytoGo(http://www.usgo.org/usa/waytogo/W2Go8x11.pdf).
(;GM[1]FF[4]CA[UTF-8]AP[CGoban:2]ST[2]RU[Japanese]SZ[19]HA[5]KM[5.50]TM[]PW[SimonCozens]PB[KeikoAihara]AB[dd][pd][jj][dp][pp];W[df];B[fd];W[cn](;B[dl])(;B[fp]CR[fp]C[Thisistheusualresponse.])(;B[co]CR[co]C[Thiswayisstrongerstill.];W[dn];B[fp]))
http://www.red-bean.com/sgf/http://www.usgo.org/usa/waytogo/W2Go8x11.pdf
-
Thislittlegameconsistsofthreemoves,followedbythreedifferentvariationsforwhathappensnext,asshowninFigure2-3.Thefiledescribesatreestructureofvariations,withparenthesisedsectionsbeingvariationsandsubvariations.
Figure2-3.Treeofmoves
Eachvariationcontainsseveralnodesseparatedbysemicolons,andeachnodehasseveralparameters.Thissortofdescriptionoftheformatisidealforconstructingatop-downparser.
Thefirstthingwe'lldoiscreatesomethingthatmerelyworksoutwhethersometextisavalidSGFfilebycheckingwhetheritparses.Let'slookatthestructurecarefullyagainfromthetopand,aswego,translateitintoagrammarsuitableforParse::RecDescent.
Let'scallthewholethingagametree,sinceaswe'veseen,itturnsouttobeatree-likestructure.Agametreeconsistsofanopenparenthesis,andasequenceofnodes.Wecanthenhavezero,one,ormanyvariationsthesearealsostoredasgametreesandfinallythere'sacloseparenthesis:
GameTree:"("SequenceGameTree(s?)")"
-
Readthisas"YoucanmakeaGameTreeifyousee(,aSe