Perl:Introduksjon

Fra CodeWiki

Gå til: navigasjon, søk

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 perl

er 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

  1. hvor er perl
  2. use strict; use warnings; kan stå på to linjer under hverandre eller sammen som her. Perl oppfatter semikolonnet som slutt på statement.
  3. Vi oppretter en leksikalsk scalar (enkeltverdi) som vi kaller $hvem og gir verdien 'Kari'
  4. 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.
  5. Vi starter en while-løkke.
    1. I vilkåret for while-løkka oppretter vi en ny leksikalsk scalar, $linje, og tilordner den verdien av <DATA>.
      1. 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.
      2. <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)
  6. chomp fjerner eventuell linjeslutt fra $linje (ulikt på windows, unix og mac - men chomp fixer det)
  7. 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).
  8. 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
  9. 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).
  10. while-løkkas slutt. Her går programflyten opp igjen til vilkåret for while-løkka, linje 5
  11. 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

  1. 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.
  2. 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.
    1. 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
  3. 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

Personlige verktøy
dataprogrammering
generelt