Seminari 8
Perl I
-Perl Bàsic-

Bioinformàtica - 1er trimestre curs 2012/2013 - UPF

Perl es un llenguatge de programació que, a mes a mes de permetre la implementació d'algorismes, també permet d'una manera senzilla la manipulació de fitxers i l'execució de comandes de unix des del nostre programa.


els programes en Perl els escriurem dins de fitxers de texte amb extensió .pl, i tots els començarem amb les següents línies, que ara mateix no és important saber què és el que fan:

#!/usr/bin/perl -w

use strict;

El nostre primer programa


ara proveu de crear un fitxer de texte per a un programa Perl, que es digui, per exemple test.pl i, a mes de les línies anteriors, escriviu:

# 18.01.2002
# el nostre primer programa!!!
#

print "hello world!\n";

on les línies que comencen amb # son línies per ficar comentaris. Enregistreu aquest fitxer i ja haureu escrit el vostre primer programa en Perl!


un programa en Perl, al igual que qualsevol altre programa, s'executa desde el shell cridant-lo pel seu nom. Els programes que nosaltres hem utilitzat fins ara han estat les comandes del Unix, com ara ls. En aquests casos, només ficant ls el programa (la comanda) ja s'executava perque el sistema, d'alguna forma, sap on es troba aquest programa. En principi, tot i que el nostre primer programa test.pl es troba probablement al mateix directori on esteu, el sistema no el trobara si simplement fiqueu:

$ test.pl

per tal de que el sistema el trobi, li heu de dir explícitament que el programa esta al directori actual. La forma de referirse al directori actual en un sistema Unix és escrivint el prefix './'. De forma que cridarem al nostre programa de la següent manera:

$ ./test.pl

segurament us haura aparegut el seguent missatge d'error:

bash: ./test.pl: Permission denied

això passa perquè tot i que el sistema ha trobat el fitxer del programa, el programa no te permís per executar-se. Afortunadament vosaltres mateixos li podeu donar aquest permís. Per tal de saber quins permisos te un fitxer, ho farem amb l'opció -l de la comanda ls:

ls -l test.pl

i ens apareixerà una línia similar a la següent:

-rw-r--r--    1 rcastelo users         210 Jan 17 17:19 test.pl

on la primera part:

-rw-r--r--

es refereix als permissos d'aquest fitxer que poden ser de tres tipus: lectura (r), escritura (w) i execució/accés (x). Un fitxer pot tenir un d'ells, dos, tots tres o cap. Com que estem en un entorn multiusuari, els permissos es poden assignar a tres categories: al propietari del fitxer, al grup d'usuaris al qual el propietari pertany, i a tothom. La part anterior de la comanda ls especifica això de la següent forma:

-rw-                     r--                      r-- 
 |||   propietari        |||   grup               |||    tothom
 |||---execucio/acces    |||---execucio/acces     |||----execucio/acces
 ||----escriptura        ||----escriptura         ||-----escriptura
 |-----lectura           |-----lectura            |------lectura

es a dir, a la primera posició s'especifica el permís de lectura, a la segona el d'escriptura, i a la tercera el d'execució/accés. En el cas de que el fitxer, és un directori aquest permís serà d'accés al directori, i en cas de que no és un directori, aquest permís serà d'execució del fitxer. Per tal de donar permís d'execució al nostre fitxer test.pl haurem d'escriure la seguent comanda:

$ chmod u+x test.pl

ara fem ls -l test.pl i hauriem de veure el seguent:

-rwxr--r--    1 rcastelo users         210 Jan 17 17:19 test.pl

aquest fitxer test.pl té ara permissos de lectura, escriptura i execució per al propietari (rcastelo), permís de lectura per al grup d'usuaris al qual rcastelo pertany (users), i permís de lectura per tothom.


ara podreu executar el vostre programa:

bash$ ./test.pl 
hello world
bash$

Declaració de variables

un programa en el qual anem a utilitzar variables necessita que, prèviament les declarem, es a dir, especifiquem la intenció d'utilitzar-les, i possiblement inicialitzar-les al mateix moment. Inicialitzar variables significa assignar-les un valor per primer cop. Per exemple, editeu el següent programa que ens preguntarà un valor i ens el mostrarà per pantalla (penseu d'escriure primer la capçalera inicial #!/usr...):

#
# declarem la nostra primera variable!
#

my $valor;

#
# cos principal del programa
#

print "entra un valor: ";

$valor = <STDIN>;

print "el valor entrat es: $valor";

enregistreu-lo sota un nom qualsevol, per exemple valor.pl. Fiqueu-li permissos d'execució i proveu-lo d'executar.

Estructura bàsica d'un programa

un programa el podem estructurar en tres parts bàsiques:

                        ------------------------
                        |                      |
                        |   ENTRADA DE DADES   |
                        |                      |
                        ------------------------
                                   ||
                                   ||
                        ------------------------
                        |                      |
                        | TRACTAMENT DE DADES  |
                        |                      |
                        ------------------------
                                   ||
                                   ||
                        ------------------------
                        |                      |
                        |   SORTIDA DE DADES   |
                        |                      |
                        ------------------------
on a l'entrada de dades el programa pregunta a l'usuari i/o llegeix d'un o mes dispositius (fitxers, web, aparells electronics, etc..) les dades que necessita per calcular allo que li demanem.


al tractament de dades el programa fa els càlculs i/o transformacions corresponents a les dades adquirides a la part d'entrada de dades.


a la sortida de dades el programa mostra, escriu o envia les dades per/a algun dispositiu (pantalla, fitxer, internet, aparell electrònic, etc..)

Operadors aritmètics

els operadors aritmètics al llenguatge Perl són els seguents:

Operador Exemple Resultat
Suma $a + $b suma de $a i $b
Resta $a - $b resta de $a menys $b
Multiplicació $a * $b producte de $a i $b
Divisió (quocient) $a / $b divisió de $a per $b
Divisió (residu, mòdul) $a % $b residu de dividir $a per $b


feu un programa que donat un valor d'entrada ens digui si és parell o senar. Seguiu l'esquema seguent:


ara feu un programa que ens pregunti dos nombres sencers i ens els sumi.


ara feu un programa que ens pregunti un nombre sencer i ens calculi el seu quadrat.


ara feu un programa que ens pregunti dos nombres sencers i ens calculi la seva mitja aritmètica.

Programa que suma nombres sencers

ara farem un programa que ens preguntarà per un número $n>0$, i ens mostrarà la suma de tots els nombres naturals fins a $n$, es a dir:


\begin{displaymath}1+2+3+4+...+(n-2)+(n-1)+n \end{displaymath}

obriu un fitxer de texte nou amb nom sumanum.pl i feu el programa en aquest fitxer. Sempre encabat de la capçalera, la part de declaració de variables i entrada de dades del programa podria ser la següent:

#
# declaracio de variables
#

my $numero;
my $suma;
my $i;

#
# entrada de dades
#

print "numero: ";

$numero = <STDIN>;

i la part de sortida de dades del programa podria ser aquesta:

#
# sortida de dades
#

print "la suma de 1 fins a $numero es: $suma\n";

heu de pensar com sera la part de tractament de les dades, es a dir, com sumar els primers $n$ nombres naturals, on $n$ estarà enregistrat a la variable $numero al tros de codi especificat anteriorment. Penseu de plantejar-vos d'avantmà quin(s) tipus de composicion(s) algorísmiques necessiteu (seqüencial, alternativa, iterativa).


pista: la suma dels primers $n$ nombres naturals es fa incrementalment utilitzant sumes parcials on la mateixa variable que enregistra el resultat $suma ens ha de servir per enregistrar les sumes parcials.


nosaltres no sumem mai, per exemple, més de dos números de cop, anem sumant de dos en dos, es a dir: 1+2=3, 3+3=6, 6+4=10, 10+5=15. Escrivim això en dues columnes fins a 5:

                $i       $suma
                 0  /-----0
                 1-- /----1
                 2--- /---3
                 3---- /--6
                 4----- /-10
                 5------  15

ens adonarem que tenim una condició inicial què és: al principi no hem sumat res ($i=0 i $suma=0). I una condició final què és: al final hem sumat 5 números ($i=5 i $suma=15. Ens hem d'adonar també que entre aquestes dues columnes i al llarg de cada suma parcial i ha una relació, una regla general que no varia, un invariant. Aquesta relació és:

                   $suma = $suma + $i;

d'aquest anàlisi heu de poder deduïr els següents fets:


un cop enregistrat i executat veurem que obtenim la suma, pero també que la sortida no té la forma que ben be esperàvem:

bash$ ./sumanombres.pl  
numero: 10
la suma de 1 fins a 10
 es: 55
bash$

això és perquè la variable $numero com a cadena de símbols conté un canvi de línia, el que li hem ficat al apretar la tecla Enter. Per tal d'eliminar aquest canvi de línia cridareu la seguent funció just despres de l'acció en que es recull el valor de $numero:

chomp($numero);

ara executeu el programa i la sortida ja hauria de ser:

bash$ ./sumanombres.pl  
numero: 10
la suma de 1 fins a 10 es: 55


Programa que calcula el factorial d'un nombre sencer positiu

feu un programa en Perl que calculi el factorial d'un nombre donat i enregistraten una variable $n.


Programa que calcula la potencia d'un nombre sencer positiu

feu un programa que ens pregunti dos números, la base ($base) i l'exponent ($exponent), i ens calculi la potència de la base elevada a l'exponent.


a l'igual que la suma, no sabem multiplicar més de dos nombres de cops (i l'ordinador tampoc), així doncs anirem calculant resultats parcials per tal de fer multiplicacions de dos en dos.


pista: utilitzarem una variable $resultat per emmagatzemar el resultat i aquesta mateixa variable ens ha de servir per emmagatzemar els productes parcials.


  1. condició inicial: qualsevol nombre elevat a 0 val 1.
  2. condició final: qualsevol nombre elevat a una potencia $n>0$ val el resultat de multiplicar aquest nombre per sí mateix $n$ cops.
  3. invariant: suposem que $base=2 i $exponent=5
                           $i     $resultat
                            0      1
                            1      2  = 2 * 1
                            2      4  = 2 * 2
                            3      8  = 2 * 4
                            4      16 = 2 * 8
                            5      32 = 2 * 16
    
    el resultat actual es el resultat anterior multiplicat per la base:
                   $resultat = $resultat * $base;