Perl:Introduksjon
Fra CodeWiki
Innhold |
Hvor er perl?
Dersom du lar programnavnet ende med .pl og første linje i hvert perlprogram ser sånn ut:
#!/usr/bin/env perler du godt i gang og kan kjøre perlprogrammet ditt på ulike operativsystemer.
use strict; use warnings
Alle Perl-programmer begynner med
use strict; use warnings;
Hvis disse to linjene mangler i toppen av Perl-programmet skal du være skeptisk. Forsøk å sette dem inn og gi kommandoen (hvis programmet heter "suspekt.pl"):
perl -c suspekt.pl
Og se om du får feilmeldinger. Hvis ikke, supert, la linjene stå. De vil gi betydelig hjelp med å forhindre at subtile programmeringsfeil sniker seg inn seinere. Hvis du får feilmeldinger, så er det stor fare for at programmet ikke gjør nøyaktig det det skal. La linjene stå og fiks feilene. Du kan få grundigere feilmeldinger ved å bytte fra "use warnings;" til "use diagnostics;" mens du feilsøker.
Lese fra en fil
while ( <> ){ # her kan vi gjøre noe for hver linje i input-fila }
Hvis vi kaller programmet lesfil.pl og datafila heter input.txt, begge filene ligger i samme katalog og det er den katalogen vi står i, så kjører vi det slik:
perl lesfil.pl input.txt
eller bare
lesfil.pl input.txt
eller:
./lesfil.pl input.txt
La oss si at input.txt har to kolonner: navn og antall og ser sånn ut:
Ola;4 Kari;5 Per;3 Kine;8 Kari;2
Kolonnene er skilt med et semikolon. Vi ønsker å finne sum antall for alle linjene hvor navn er 'Kari'
#!/usr/bin/env perl use strict; use warnings; my $hvem = 'Kari'; my $sum = 0; while (my $linje = <> ){ chomp $linje; next unless $linje; my ($navn, $antall) = split /;/, $linje; $sum += $antall if $navn eq $hvem; } print "Sum for $hvem er: $sum \n";
og kjører det:
lesfil.pl input.txt
Så bør resultatet bli:
Sum for Kari er: 7
Skalerer
Hvis du nå tar en kopi av input-fila, og kaller den input2.txt kan du kjøre kommandoen
lesfil.pl input.txt input2.txt
eller
lesfil.pl input*
så summerer den på tvers av begge (alle) filene. Praktisk.
__DATA__
For eksemplene videre så vil vi la dataene programmet skal lese inn ligge i slutten av fila. Dette er praktisk både når det er behov for å fylle noen datastrukturer ved oppstart, eller dersom du skal spørre om hjelp (f.eks. på forumet her) med et program som trenger litt inputdata for å demonstrere problemet du møter, i stedet for å sende med inputfil:
#!/usr/bin/env perl use strict; use warnings; my $hvem = 'Kari'; my $sum = 0; while (my $linje = <DATA> ){ chomp $linje; next unless $linje; my ($navn, $antall) = split /;/, $linje; $sum += $antall if $navn eq $hvem; } print "Sum for $hvem er: $sum \n"; __DATA__ Ola;4 Kari;5 Per;3 Kine;8 Kari;2
Forklaring av koden for lesfil.pl
- hvor er perl
- use strict; use warnings; kan stå på to linjer under hverandre eller sammen som her. Perl oppfatter semikolonnet som slutt på statement.
- Vi oppretter en leksikalsk scalar (enkeltverdi) som vi kaller $hvem og gir verdien 'Kari'
- Vi oppretter en ny scalar, $sum, og gir den verdien 0. Når verdien skal være 0 i starten kunne vi latt være å tilordne verdien, men det er best å gjøre det til en vane å være tydelig på intensjonene.
- Vi starter en while-løkke.
- I vilkåret for while-løkka oppretter vi en ny leksikalsk scalar, $linje, og tilordner den verdien av <DATA>.
- Ved å oprette $linje her, så forsvinner den ved slutten av while-løkka. Dermed unngår vi å fylle opp med variabler som er tilgjengelige i deler av programmet der de ikke skal benyttes.
- <DATA> sørger for at en record leses fra angitt input, her DATA-seksjonen på slutten av fila. Dersom det lykkes å lese noe, får $linje en verdi og programflyten tar en (ny) runde i løkka, ellers avsluttes while og programflyten fortsetter etter while-løkka. (I det første eksemplet leste vi fra <> - stdin)
- I vilkåret for while-løkka oppretter vi en ny leksikalsk scalar, $linje, og tilordner den verdien av <DATA>.
- chomp fjerner eventuell linjeslutt fra $linje (ulikt på windows, unix og mac - men chomp fixer det)
- dersom det ikke er noe igjen i $linje når linjeskift er fjernet, så tar vi neste record. Dvs at vi går direkte opp til vilkåret for løkka igjen (der perl trolig finner ut at det ikke er flere linjer).
- vi oppretter en liste med variabler, derfor setter vi dem i parentes, og gir dem verdien av å splitte på regexp /;/ (semikolon) det som er i variabelen $linje
- så legger vi sammen det som er i $sum og det som er i $antall og tilordner resultatet til $sum. Alt dette skjer bare dersom $navn er identisk med det som vi har i $hvem (Kari). (eq står for equal og er == for strenger).
- while-løkkas slutt. Her går programflyten opp igjen til vilkåret for while-løkka, linje 5
- Når det ikke lenger gikk å lese mer input skriver vi resultatet til stdout - konsollet vanligvis.
Tilgang til hjelp
Perl er usedvanlig godt dokumentert. Fra kommandolinjen kan du få hjelp med det aller meste Perl, det kan være i overkant overveldende i starten, men forsøk:
perldoc perldoc
gir oversikt over hvordan kommandoen perldoc fungerer. Forsøk f.eks.
perldoc -f split
for hjelp med funksjonen "split".
Har du installert distribusjonen fra ActiveState har du komplett dokumentasjon av Perl og alle modulene som ActiveState har pakket med som html-sider - se under Start->Programmer->ActiveState Andre distribusjoner tilbyr det samme, men typisk som en egen installasjonspakke. perldoc er alltid med, uansett.
Fungerer det?
Fungerer denne beskrivelsen? Kommenter gjerne her, eller på diskusjonssiden
Videre, ikke bare Kari
Noen ganger er vi interessert i å få ut en oppsummering for hver person. Da er assosiasjonslager, hash i Perl, et kjekt verktøy.
#!/usr/bin/env perl use strict; use warnings; my %antall_for; while (my $linje = <DATA> ){ chomp $linje; next unless $linje; my ($navn, $antall) = split /;/, $linje; $antall_for{$navn} += $antall; } print "Antall \n"; print " $_: \t$antall_for{$_} \n" for sort keys %antall_for; __DATA__ Ola;4 Kari;5 Per;3 Kine;8 Kari;2 Ola;1
Som gir:
Antall Kari: 7 Kine: 8 Ola: 5 Per: 3
Hvis vi nå hadde hatt flere ulike filer med forskjellig type informasjon, men felles entydige nøkler (her navn) på tvers av filene, så kunne vi lett ha flettet dataene sammen.
Forklaring av koden
- I stedet for å deklarere to variabler ($hvem og $sum), lager vi her et hash som vi kaller %antall_for. %-tegnet forteller Perl at vi ønsker et hash. Navnet %antall_for gjør det lett å huske at vi bruker det til å assosiere antall med nærmere angitte id'er, her navn.
- Inne i løkka der vi leser fra fila bruker vi navnet (første felt) som nøkkel, og dersom Perl ser at det ikke finnes noe for den nøkkelen fra før, så opprettes elementet automatisk. Når vi da første gang legger et antall til en verdi som ikke fantes tidligere godtar Perl det og later som den gamle verdien var 0.
- Selve hash'et er %antall_for, men verdien for f.eks. Kari er en scalar, en enkeltverdi, derfor adresserer vi det som $antall_for{Kari} += $antall
- Til slutt skriver vi ut en liten rapport alfabetisk sortert:
print " $_: \t$antall_for{$_} \n" for sort keys %antall_for;
Kunne ha vært skrevet mer omstendelig:
for my $hvem (sort keys %antall_for){ print " $hvem: \t$antall_for{$hvem} \n"; }
Men det blir ikke hverken klarere eller bedre av det, så det gjør vi ofte ikke.
Neste eksempel
Noen ønsker?
Eksterne lenker
Fullstendig Perl-dokumentasjonen på perl.org
