Foiler fra Perl-kurset 2004-10-28

Her er foilene fra perl-kurset 28. oktober 2004. HTML-en er dessverre full av ekle ting, men det var nå sånn vim genererte den. Fy på vim!

#!/usr/bin/perl
use strict;
use warnings;

my $msg = <<'EOF';



    __     __   _ _                                        
    \ \   / /__| | | _____  _ __ ___  _ __ ___   ___ _ __  
     \ \ / / _ \ | |/ / _ \| '_ ` _ \| '_ ` _ \ / _ \ '_ \ 
      \ V /  __/ |   < (_) | | | | | | | | | | |  __/ | | |
       \_/ \___|_|_|\_\___/|_| |_| |_|_| |_| |_|\___|_| |_|
                                                           
             _   _ _   ____           _       _                   _ 
            | |_(_) | |  _ \ ___ _ __| |     | | ___   _ _ __ ___| |
            | __| | | | |_) / _ \ '__| |_____| |/ / | | | '__/ __| |
            | |_| | | |  __/  __/ |  | |_____|   <| |_| | |  \__ \_|
             \__|_|_| |_|   \___|_|  |_|     |_|\_\\__,_|_|  |___(_)
                                                                    



EOF

print $msg;

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict; use warnings;
print <<'EOF';
                              HVA ER PERL?
    
  * Betyr: Practical Extraction and Reporting Language
  * Alternativt: Pathologically Eclectic Rubbish Lister
  * Et cross-platform scriptingspråk skrevet av Larry Wall (han er kul)
  * Virker i UNIX, Windows, på Mac, i VMS, etc etc
  * Kildekoden til perl er åpen (GPL)
  * Et tolket språk (kompileres ikke, "kompileres" hver gang det kjøres).

                              HVORFOR PERL?

  * Veldig kraftig, du kan gjøre mye med lite kode
  * Kjapt å skrive
  * Svært mange ferdige biblioteker ( http://search.cpan.org )
  * Det er kult

                            HVORFOR IKKE PERL?
  
  * Ting kan fort bli litt grisete om man er syk i hodet eller ikke tenker
  * Veldig mye ulik syntaks (flere måter å gjøre det samme på)
  * Det blir en del krangling med Python-folk
  * Python-folkene kommer sikkert på mer...

EOF

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

print <<'EOF'

                        HVA VI SKAL HA I DETTE KURSET

   Det finnes to typer perl:
   * Oneliner-perl (kjappe triks man bare skal bruke en gang (eller ha på
     t-skjorta))
   * Produksjonskode-perl (relativt pen, leselig kode)

   Ikke alle ser denne forskjellen. De som ikke ser denne forskjellen er
   grunnen til at perl har rykte på seg for å være uleselig og grisete,
   såkalt "write once".

   Jeg er ikke så glad i dem som ikke ser forskjellen. Spesielt ikke når jeg
   må fikse på koden deres.

   I dette kurset skal vi lære om produksjonskode-perl. Oneliner-perl lærer
   dere tidsnok selv ;-)

EOF

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

print <<"EOF"
# Enkel syntaks:

 * Minner veldig om C, PHP og Java. 
 * { og } deler inn blokker, 
 * ( og ) rundt argumenter, etc (MEN: valgfrie). 
 * Alle statements avsluttes med semikolon
 
EOF

# Parenteser rundt er valgfrie. Begge disse virker:

print "hei\n";
print("hei\n");

# Parenteser brukes for å angi skop. Dette er spesielt nyttig når man har 
# flere kommandoer nøstet inni hverandre. Lurer du på om det funker uten 
# parentes? Da beholder du parentesene. Lesbar kode er viktig.

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

## Variabler og slikt
# Variabler, arrays og hasher deklareres med ordet "my". Det holder med ett 
# ord for å deklarere, siden Perl ikke bryr seg så mye om innholdet i 
# variabler. Om det er et heltall, et desimaltall eller tekst tas litt på
# gefühlen.

# En vanlig variabel (såkalt "skalar):
my $tall = 15;
my $tekst = 'hei';

# En array:
my @array;
@array = (15, 'hei');    # Definer alle verdier
$array[2] = 'gunnar';    # Sett tredje element

# En hash:
my %hash = ('ottar' => 15, 'pelle' => 17);  # Definer alle verdier
$hash{'bjarne'} = 13;                       # Sett verdi for bjarne


# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict; use warnings;

## Quoting
# Ulike hermetegn oppfører seg forskjellig, på samme måte som i PHP 
# (PHP rappet det av Perl). 

my $test = 'ottar';

print "hei $test\n";   # printer dette: hei ottar
print 'hei $test\n';   # printer dette: hei $test\n



# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict; use warnings;

## Hvordan slå sammen tekst (også likt som i PHP, av samme grunn)

# Flere tekststrenger slås sammen med punktum (Java bruker pluss).
my $fornavn = "Kjell Magne";
my $etternavn = "Bondevik";
my $fulltnavn = $fornavn . ' ' . $etternavn;

# Vi kunne selvsagt også brukt dette, som også er mye vanligere å bruke:
$fulltnavn = "$fornavn $etternavn";

# Men av og til er "" upraktisk, som f.eks. om du skal printe ut HTML, 
# eller har verdiene i noe annet enn variabler.
my %verdier = ('uid' => 1015, 'bnavn' => 'kjellmangle');

# Dette:
print '<a href="' . $verdier{'uid'} . '">' . $verdier{'bnavn'} . "</a>\n";

# Er penere enn dette:
print "<a href=\"$verdier{'uid'}\">$verdier{'bnavn'}</a>\n";

# Men begge virker, selv om highlightingen min ikke tror det.
# At noe virker men blir highlightet feil er ofte et hint. Ta hintet.

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict; use warnings;

## Ting på flere linjer: bruk enten punktum eller <<"EOF";.

# Punktum (Som regel best):
my $test = "ottar";
my $langvariabel = "Denne teksten på flere linjer er dedikert til\n"
                 . "min gode venn og følgesvenn $test\n";

# <<"EOF"; (Bruk bare når det er _store_ greier, og punktum blir veldig
# tungvint. Ikke spesielt viktig å kunne, men greit å vite om):

$langvariabel = <<"EOF";
Denne teksten på flere linjer er dedikert til
min gode venn og følgesvenn $test
EOF

$langvariabel = <<'EOG';
Denne teksten på flere linjer er dedikert til
min gode venn og følgesvenn $test
EOG

# Det inni hermetegnene er et merke. Alt helt ned til merket blir med i 
# variabelen. Hva som skjer med teksten inni avhenger av hermetegnene rundt 
# merket. Husk å bruke samme merke bare en gang! Pass på semikolonet!

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

## Logikk med tallverdier
# Logikk i perl ligner veldig på andre språk som C, PHP og Java.
# 0, tom streng og udefinert er false, det meste annet er true (som i PHP).
# IKKE utnytt dette for mye. Det blir vanskelig å lese. Test bare rett på 
# variabelen om du bruker variabelen som en boolean.

# Booleans (1 = sant, 0 = usant):
my $boolean = 1;
if ($boolean) {
    print "\$boolean er sann!\n";  # Dette skjer.
} else {
    print "\$boolean er usann!\n"; # Dette skjer ikke.
}

my $tall = 15;
if ($tall > 10) {
    print "\$tall er over 10!\n";
}
elsif ($tall < 5) {                # elsif står for "else if"
    print "\$tall under over 5!\n";
}

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

## Logikk med tekstverdier

# Dere som kan PHP er vant til å bruke "==" på tekststrenger. Det virker 
# IKKE i Perl! I Perl er det egne greier for å sammenligne ord.

# De viktigste:
#  eq: er lik
#  ne: er ikke lik

my $tekst = "banan";
if ($tekst eq "banan") {
    print "\$tekst er lik banan\n";
}

if ($tekst ne "eple") {
    print "\$tekst er ikke lik eple\n";
}

if (length($tekst) > 3) {
    print "\$tekst er mer enn tre tegn lang\n";
}

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

## While-loop
# En while-loop i perl virker akkurat som i andre språk. Yay!

my $counter = 0;
while ($counter < 10) {
    print "tallet er $counter\n";
    $counter++;                       # Øker $counter med 1
}

## For-loop
# Virker også som i andre språk! Hurra!

for (my $tall = 0; $tall < 10; $tall++) {
    print "tallet er $tall\n";
}

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

## Foreach-loopen
# Denne er litt spesiell. Man har noe lignende i PHP, men syntaksen er ulik.
# Foreach går gjennom hvert element i en liste.

my @array = ('gunnar', 'ottar', 'per ulf');

# Legg merke til at løpevariabelen $verdi angis utenfor parentesen!
foreach my $verdi (@array) {
    print "$verdi\n";
}

# Man kan også bruke foreach til å løpe over alle nøklene i en hash:
my %hash = ( 'uid' => 3425, 'bnavn' => 'ottar', 'rnavn' => 'Ottar Evje' );

# Kommandoen "keys" gir ut en array med alle nøklene i hashen, så dette er 
# egentlig akkurat det samme.
foreach my $key (keys %hash) {
    print $key . " = " . $hash{$key} . "\n";
}

# Foreach er dritnyttig!

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict; use warnings;

## Å bryte ut av looper i Perl
# Når man skal bryte ut av looper er Perl litt rart. Alt heter noe annet enn
# det gjør i andre språk! Dette er jo skikkelig teit, men man venner seg
# til det ganske fort. Jeg tipper grunnen er at perl ble laget for å parse
# tekst, og da virket disse navnene mer logiske.
#
# En liten tabell: 
#     +------+-------------+---------------------------------------------+
#     | Perl | Andre språk | Betydning                                   |
#     +------+-------------+---------------------------------------------+
#     | next | continue    | Hopp over det som er nedenfor og kjør neste |
#     |      |             | runde i loopen                              |
#     +------+-------------+---------------------------------------------+
#     | last | break       | Avbryt kjøringen av hele loopen             |
#     +------+-------------+---------------------------------------------+

# Eksempel på next:
foreach my $tall (1, 2, 3) {
    if ($tall == 2) {
        next;
    }
    print "tallet er $tall!\n";
}

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

# Eksempel på "last": Rakettutskyting med forviklinger

for (my $tall = 10; $tall > 0; $tall--) {
    print "$tall...\n";

    # Oh no! Når vi kommer til 5 eksploderer raketten!
    if ($tall == 5) {
        print "Boom!\n";
        last; # Her avbryter loopen
    }
}

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

### Hva er egentlig det greiene på toppen av alle foilene?

## Shebangen
#
# #!/usr/bin/perl er en såkalt shebang. Den forteller hva slags språk
# scriptet er skrevet i, og hva slags program som skal brukes til å tolke 
# det.
#
# Om ikke shebangen var det ville vi måttet vite hva scriptet var skrevet i,
# og selv kjørt programmet med filen som argument.
#
# Uten shebang:
#   perl program.pl
#
# Med shebang:
#   ./program.pl
#
# For at programmet skal kunne kjøres direkte må det også være kjørbart.
# "chmod +x program.pl" fikser den biffen.

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

## use strict:
#
# Uten use strict trenger man ikke å deklarere variabler. Variabler blir 
# bare opprettet når de trengs av seg selv. Kjekt? Nei.
#
# Alle variabler som er implisitt deklarert blir nemlig globale. 
# Globale variabler er ikke bra.
#
# Dessuten blir det plutselig mulig å bruke samme variabel til flere ting 
# uten å merke det. Det er heller ikke bra.


# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use warnings;

# Eksempel uten strict. Merk at det ikke er noen "use strict" øverst.
# Vi tenker oss en webtjeneste der brukeren kan logge inn. Brukeren er 
# innlogget som "rob-geir".
$user = "rob-geir";
print "Du er logget inn som: $user\n";

# maaaange linjer kode med magi og ting. Vi har helt glemt $user.

# Hoi, systemet har en funksjon som lister opp alle som er innlogget.
# Akkurat nå er det disse tre:
@users = ('ottar', 'rob-geir', 'dan-børge' );

# Vi lister opp alle brukerene. Men hold an! Her brukte vi jo $user på nytt!
print "Brukere innlogget: ";
for ($i = 0; $i < scalar @users; $i++){
    $user = $users[$i];
    print "$user ";
}
print "\n";

# Maaange linjer med kode. Nå har vi glemt brukerlisten.

# Nederst på siden vil vi vise hvem som er logget inn en gang til.
print "Du er logget inn som: $user\n";

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use warnings;

# Warnings betyr akkurat det som står: Advarsler. Det gjør at perl advarer 
# når noe ser rart ut. Du vil ha det. Om koden din gir advarsler er det fordi
# du har gjort noe du ikke burde gjøre.
#
# Jeg fjernet use strict denne gangen, så det skulle bli lettere å gjøre
# noe dumt warnings kunne oppdage.

# Eksempel på hvorfor warnings er bra:

my $foo = "bar";

print "hei, jeg er $bar\n"; # Å nei! Feil variabelnavn!

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

## Kompliserte datastrukturer i Perl
# Perl versjon 4 (som ingen bruker lenger) hadde ingen støtte for å nøste
# arrays og lignende. Todimensjonale arrays var ikke mulig. Folk brukte all
# verdens grisete triks for å få til noe tilsvarende. Æsj!
#
# Perl versjon 5 har egentlig heller ikke støtte for flerdimensjonale arrays
# eller sånne ting, men det merker vi ikke stort til. Nå har vi nemlig pekere!
#
# Pekere i perl kan minne om pointere i C eller objektreferanser i Java.

# Vi har arrayen @array
my @array = ('ottar', 'spiser', 'fisk');

# Vi lager en referanse
my $ref = [ @array ]; # Firkantparenteser til array, akkurat som ved henting
                      # av verdier

# Vi får tilbake arrayen med @{}:
my @sammearray = @{$ref};  # denne sier: $ref er egentlig en array, altså!

# Det samme gjelder for hasher, men med { %hash } og %{$ref}

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

# Vi kan også gjøre ting litt mer direkte, uten å lage @array først:
my $arrayref = [ 'geir', 'liker', 'sko' ];

# Og så kan vi hente ut verdier direkte på to måter. De er omtrent like bra,
# så det er bare å velge den du liker best. Personlig derefererer jeg gjerne 
# referansen og bruker arrayen/hashen direkte etterpå, som på forrige foil.
print @{$arrayref}[0] . "\n"; # Bruker @{} som før
print $arrayref->[0] . "\n";  # Derefererer peker med ->, som i C.

# Det samme virker for hasher:
my $hashref = { 'name' => 'ottar', 'liker' => 'sko' };
my %hash = %{$hashref};

print %{$hashref}->{'name'} . "\n"; # Perl liker ikke }{, så vi må ha pil.
print $hashref->{'name'} . "\n";
print $hash{'name'} . "\n";

# Legg merke til at den første bare er stygg, og de to siste ligner ganske
# mye på hverandre. Ukritisk bruk av referanser kan være en potensiell
# feilkilde, siden det er lett å blingse. Bare lag en vanlig hash eller
# array, så unngår du problemet.

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

## Så, til poenget: Hvordan lage todimensjonale ting?
# Todimensjonale arrays i Perl 5 er en array med arrayreferanser i.
# Hva merker vi til dette? Ingen ting. Perl fikser det for oss automatisk.

my @array;
$array[0] = [ 'rob-geir' ]; # Vi legger en array-referanse fremst i arrayen.

# Om vi printer ut $array[0] nå, får vi bare en referanse, omtrent som når vi
# printer et rart objekt i Java. Den ser slik ut: "ARRAY(0x814cd28)"
print $array[0] . "\n";

# Dette er altså egentlig en referanse. Men vent nå litt, da kan vi jo bruke
# pil-notasjonen fra forrige foil!
print $array[0]->[0] . "\n";

# Men denne pilen er jo kjedelig å skrive. Det fikser Perl! Det virker nemlig
# uten også!
print $array[0][0] . "\n";

# Og vips, vi merker ingen ting til dette rare referanse-greiene!
# Men, det er jo kjekt å vite om dem. Vi kan nemlig bruke dem andre steder
# også.

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

# Men av og til må vi likevel vite om dette rare med pekerene. Si vi lager en
# todimensjonal array med navn i:
my @array = (
        [ 'gunnar', 'ottar', 'bengt-åke' ],
        [ 'kåre', 'ole-rånni', 'per' ],
        [ 'kåbbai-laila', 'per-kristian foss' ]
    );

# Og så vil vi gå gjennom alle verdiene:
foreach my $ref (@array) {
    my @navn = @{$ref};   # Foreach gir oss referansen, så vi lager en array
    foreach my $navn (@navn) {
        print "$navn\n";
    }
    print "\n";           # Printer et linjeskift etter hver gruppe
}

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

## Å lese inn ting med perl
# Noe av det man gjør oftest i perl er å lese inn data. Dette gjør man med 
# "<>". Omtrent sånn:

## Lese fra standard in
# En og en linje:
while (my $linje = <STDIN>) {
    print $linje;
}

# Alt inn i en stor array (krever mer minne, unngå om du kan):
my @array = <STDIN>;
foreach my $linje (@array) {
    print $linje;
}



# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

## Å lese fra en fil:
# Åpne filen, eller dø om det ikke virket.
open(my $FILDESKRIPTOR, "test.txt") or die("Kunne ikke åpne test.txt: $!");

# Les, og print ut med "> " foran:
while(my $linje = <$FILDESKRIPTOR>) {
    print "> $linje";
}

# Lukk.
close($FILDESKRIPTOR);

# Hett tips: open kan også brukes for å åpne ting for skriving.
# Kjør kommandoen "perldoc -f open" for å få masse info om alt du
# kan gjøre med open.

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

## Fjerne linjeskift fra det du leser
# Til forskjell fra f.eks. sed og grep, fjerner ikke perl linjeskift av seg
# selv. Om du ikke vil ha dem, må du fjerne dem.
# Til dette har vi kommandoen chomp.

while (my $linje = <STDIN>) {
    chomp($linje);
    print "$linje\n"; # ok, litt unyttig å bare sette på igjen linjeskiftet.
}

# chomp kan også kjøres rett på en hel array. Da fikser den alle verdiene.

my @array = <STDIN>;
chomp(@array);

# NB! Det er veldig vanlig å glemme chomp, og så fortsette i god tro.
# Det kan føre til ekle bugs der linjeskift dukker opp på rare steder.
# Jeg vet dette fordi jeg gjør det hele tiden selv ;-)

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

## Printing i Perl
# Hittil har vi bare sett på basic bruk av print. Men print kan brukes til 
# mer. For eksempel en fildeskriptor:

# Dette burde gi morosakene ut på standard error.
# PS: Det er IKKE komma etter fildeskriptoren når du printer!
print STDERR "Aaaah! Venstre motor brenner!\n";


# Om du åpner noe med skrivetilgang med open kan og også bruke print der.
# Dette overskriver test.txt:
open(my $DESKR, ">test.txt") or die ("feil ved åpning av test.txt: $!");

print $DESKR "Hei, har du det bra?\n";

close($DESKR);


# Print på en array printer alle verdiene:
my @linjer = ("hei\n", "ost\n", "banan\n");
print @linjer;

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict; use warnings;

## Printf: Fancy-schmancy printing
# Der sitter du med et digert desimaltall, og vil printe det ut med to
# desimaler. Printf to the rescue!
my $tall = 3.1415;
printf("pi: %.2f\n", $tall);

# Eller du har en masse tallverdier med ulik lengde, og vil høyurejustere:
my @tall = (3, 1337, 25, 254);
foreach my $tall (@tall) {
    printf("tall: %4d\n", $tall);  # juksa litt: hardkodet inn maksbredde fire
}

# Eller du vil printe ut et tall i hex, oktal og desimal, null-paddet til 
# åtte sifre:
printf("hex: %08x %08o %08d\n", 255, 255, 255);

# Du kan også lagre resultatet i en annen variabel med sprintf:
my $leet = sprintf("%03.2f", 13.37890);
print $leet, "\n";
# Dette er litt for omfattende til at vi kan herje veldig mye med det.
# "perldoc -f sprintf" har mer info.
#
# Pass på! Det er lurere å bruke print om du ikke trenger ekstra formatering!
# Printf er tregere, og det er lettere å gjøre feil.

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict; use warnings;

## Splitting av tekststrenger
# Ofte har man en lang greie man vil splitte inn i en array. No problem!

my $string = "www.pvv.ntnu.no";
my @deler = split(".", $string);
# nå inneholder @deler dette: ('www', 'pvv', 'ntnu', 'no');

# Man kan også splitte på flere tegn:
my $verdier = "en, to, tre, fire, fem";
my @verdier = split(", ", $verdier);

# I stedet for å skrive en string som tegnet man kan splitte på, kan man også
# skrive et regulært uttrykk. Vi tar kanskje noe om regulære uttrykk senere,
# hvis vi rekker det.

# Å splitte på " " (mellomrom) er litt spesielt. Da oppfører perl seg som awk,
# og gir deg alle ordene, uansett hvor mye whitespace det er mellom. 
my $setn = "hei, jeg  er    ikke så   flink    med      mellomrom";
my @setn = split(" ", $setn);

# Om du vil at det skal bli som i cut, med blanke felter mellom spaces som er 
# inntil hverandre, bruk regulæruttrykket / /:
@setn = split(/ /, $setn);

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

## Join, det motsatte av split
# Hvis du har en array, og skal lage en tekststreng av verdiene, er join
# tingen for deg. Den setter sammen alle feltene, med en tekststreng mellom.

my @koner = ("magda", "gunhild", "gunda");
my $penliste = join(", ", @koner);
print "Koner: $penliste\n";

# Ut kommer dette: "Koner: magda, gunhild, gunda"


# Gammelt jegertriks for å lage en diger variabel av en array du har lest inn:
my @linjer = <STDIN>;
my $digervariabel = join("", @linjer); # Vi joiner på tom streng

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

## Magiske variabler i Perl
# De magiske variablene i Perl er beryktet, og flittig kritisert av 
# Python-brukere. Og ja, de er litt ekle. Ikke bruk dem mer enn du må,
# og husk å kommentere kode der du eventuelt bruker dem.

# Disse tre dukker opp relativt ofte, og man forventes vel egentlig å kunne
# dem, så de trenger man ikke forklare i koden. Men husk å forklare eventuelle
# andre utskeielser!

    $_ # Default-variabelen. Om ikke noe er angitt, brukes som regel
       # denne. Kan stort sett unngås, men av og til lurer den seg inn.

    @_ # Default-arrayen. Dukker stort sett bare opp som argumentene til
       # subrutiner (mer om dem senere, om vi rekker det)

    $! # Feilmelding. Denne variabelen inneholder siste feilmelding.
       # Den ble brukt etter open for et par foiler siden.

# Det er hauger av andre magiske variabler også, men disse tre får holde 
# inntil videre. Er du nysgjerrig på mer, kjør kommandoen "perldoc perlvar".

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

## Subrutiner (også kjent som funksjoner eller metoder)
# Subrutiner er veldig kjekke for å strukturere scripts. De har litt
# annerledes syntaks enn det vi er vant til, men ikke så veldig.

# Vi lager en subrutine
sub hei () {
    print "hei!\n";
}

# ...og vi kjører den.
hei();

# ...og en gang til:
hei();

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

# Men vi vil kanskje gjerne gi subrutinen noen argumenter. For å fortelle 
# subrutinen at den skal ta inn to variabler, gir vi den to $-tegn i 
# parentesen:

sub hei($$) {
    my ($navn, $adjektiv) = @_;            # Oi, hva er dette?

    print "Hei $navn, du er $adjektiv!\n";
}

# Og så hilser vi på noen
hei("ottar", "kul");
hei("pål", "illeluktende");

# Nå dukket det opp litt nytt, spesielt den rare greia som heter @_. 
# @_ er en magisk array der alle argumentene ligger. Jeg vet at det er to 
# argumenter (det står jo "$$" i parentesen), så jeg kan bare hente dem ut 
# direkte med to variabler inni en parentes.
#
# Denne parentesen er kanskje litt rar, men det er egentlig bare 
# array-snarveien baklengs. Husk at vi kan si 
my @array = ('per', 'pål');

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

# Vi vil jo ofte returnere noe fra subrutinene våre. Det er lett. Bare bruk 
# return $variabel:

# Vi lager subrutinen fjortis, som setter masse utropstegn bakerst på
# tekst:
sub fjortis ($) {
    my ( $tekst ) = @_;
    return "$tekst!!!11";
}

# Hent ut fjortis-tegnsatt variabel
my $fjortis = fjortis("hei");

# Print den.
print "$fjortis\n";

# Print en dobbeltfjortis variabel
print fjortis(fjortis("hallo")) . "\n";

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

# Å sende inn og ut arrays fra subrutiner er lett:

# En skikkelig unødvendig og teit subrutine som sorterer mot alfabetet:
sub descsort (@) {
    my @array = @_;
    my @output = reverse sort @array;
    return @output;
}

my @array = ('hei', '', 'aha');
foreach my $ord (descsort(@array)) {
    print "$ord\n";
}


# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

# Å leke med en hash er også temmelig likt:

sub print_hash (%) {
    my %hash = @_;                 # Hold an! Hva er dette? Hash = Array? Hæ?

    foreach my $key (keys %hash) {
        print "$key = " . $hash{$key} . "\n";
    }
}

my %hash = ( 'fnavn' => 'herodes', 'enavn' => 'falsk' );
print_hash(%hash);

# Dette med "%hash = @_" virker jo litt sært. Men legg merke til at vi bruker
# de samme parentesene for å lage arrays og hasher. Faktisk er det bare piler
# stedet for kommaer på annenhver for at det skal bli lettere å se hva som er
# hva.
#
# Når du legger masse verdier inn i en hash gir du den altså bare en array
# med verdier, og så blir annenhver verdi nøkkel og innhold.
# (dette virker på samme måten i PHP).

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

# Men hva om vi for eksempel vil gi inn to variabler og en array?

sub felleseie ($$@) {
    my $han = shift;              # Hva er denne shift?
    my $hun = shift;
    my @eiendeler = @_;
    print "$han og $hun eier sammen dette:\n";
    foreach my $ting (@eiendeler) {
        print "\t$ting\n";
    }
}

my @eiendeler = ('sofa', 'tv', 'stereo');
felleseie("per", "kari", @eiendeler);

# shift(@array) tar første verdi i @array, fjerner den fra arrayen og
# returnerer den. ('en', 'to', 'tre') blir altså ('to', 'tre') etter shift.
# Slik kan vi fjerne de to første variablene, og sitte igjen med resten, som
# er til arrayen.
# 
# Grunnen til at det må gjøres slik er at @_ bare inneholder alle verdiene, 
# den har ikke system på hva som er hva.

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

# Men hva med to arrays? @_ vet jo ikke når den ene slutter og den andre 
# begynner! Da må vi til med referanser igjen!

sub tell_penisforlengere (\@\@) {
    my @biler = @{ $_[0] };
    my @baater = @{ $_[1] };

    print "Det er ".scalar @biler." bilær og ".scalar @baater."båtær\n";
}

my @biler = ('mercedes', 'porsche', 'austin martin');
my @baater = ('sjekte', 'campingcruiser', 'yacht', 'pråm');

tell_penisforlengere(\@biler, \@baater);

# Hva er disse backslashene? Bare en annen måte å lage referanser på.
# I C ville de tilsvart '&var'. Hvilken du bruker er hipp som happ. 
# Disse gjør det samme:
my $biler;
$biler = [ @biler ];
$biler = \@biler;

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

# Hittil har vi hatt funksjonen øverst i scriptet. Men da må vi jo scrolle
# forbi masse drit for å se hva selve programmet gjør (man *skal* gi 
# subrutinene så logiske navn at man skjønner hva de gjør ut fra navnet.

# Dette fikser vi med en prototype. De er omtrent som i C:
sub hei ($$);

# Og så bruker vi den
hei('rob-geir', 'tøff');
hei('gunnar', 'sexy');

# Og her kommer den.
sub hei ($$) {
    my ($navn, $adjektiv) = @_;
    print "Hei $navn, du er $adjektiv!\n";
}

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

# Om du skriver rekursive funksjoner må du uansett prototype. 
# La oss se på denne greia som teller nedover (på en skikkelig teit måte):

sub tellned ($);

sub tellned ($) {
    my $tall = shift;

    print "$tall...\n";

    if ($tall > 0) {
        tellned($tall-1);
    }
}

tellned(10);

# ----------------------------------------------------------------------------------

#!/usr/bin/perl
use strict;
use warnings;

## Noen siste ord om subrutiner
# 
# I en del gammel eller dårlig kode vil du kanskje oppdage at alle 
# subrutinekall er med '&' foran, og at ingenting er prototypet.
#
# Dette var slik subrutiner virket før. De sjekket ikke antall argumenter 
# eller noe som helst, og man sa fra om at de var subrutiner med &.
# Det er fremdeles mulig å gjøre det slik av hensyn til bakoverkompatibilitet,
# men IKKE GJØR DET!
#
# Om man gjør dette forsvinner nemlig alle former for feilsjekking rett i
# rett i dass, siden &subrutine() slår av sjekking av antall argumenter og
# slikt. Dette er et kjempetriks for å skjule bugs. Just don't do it, okay?

# ----------------------------------------------------------------------------------