Está en la página 1de 18

Capı́tulo 4

Fundamentos de programación
Eduardo Andrés León y José Marı́a Fernández

4.1. Introducción

La Bioinformática puede ser un término muy amplio y difı́cil de resumir. Podrı́amos decir que es el
uso del hardware y software de un ordenador para analizar e interpertar datos biológicos. Ya seamos
matemáticos, fı́sicos, informáticos, biólogos o quı́micos, serán necesarios unos conocimientos mı́nimos
tanto de programación, como de biologı́a. Pero, antes de nada respondamos dos preguntas básicas en
bioinformática. ¿Qué es un programa? Un programa es un conjunto de instrucciones en un particular
lenguaje de programación que puede ser interpretado por un ordenador. ¿Qué es un lenguaje de pro-
gramación? Se podrı́a definir como el conjunto de instrucciones necesarias para escribir un programa.
A través de esta sintaxis podremos comunicarnos con nuestro ordenador para que realice determina-
das tareas por nosotros. Muchos de los lenguajes de programación son parecidos al lenguaje humano,
pero esta definidos de forma mucho más estricta. En este capı́tulo hablaremos más extensamente del
lenguaje de programación Perl. Pero primero veamos los tipos de lenguaje de programación existentes.

4.2. Tipos de lenguajes de programación

4.2.1. Abstracción

Existen dos tipos de lenguajes claramente diferenciados: los lenguajes de bajo y alto nivel. El ordenador
sólo entiende un lenguaje conocido como código binario o código máquina, consistente en ceros y unos.
Es decir, sólo utiliza 0 y 1 para codificar cualquier acción. Los lenguajes más próximos a la arquitectura
binaria de hardware se denominan lenguajes de bajo nivel y los que son más sencillos y se encuentran
más cercanos al lenguaje humano se denominan lenguajes de alto nivel.

Lenguajes de bajo nivel

Son lenguajes totalmente dependientes de la máquina, es decir, que los programas que se realizan con
este tipo de lenguajes no se pueden utilizar en otras máquinas al estar prácticamente diseñados a
medida del hardware, aprovechan al máximo las caracterı́sticas del mismo.

99
Dentro de este grupo se encuentran el lenguaje máquina, este lenguaje ordena a la máquina las ope-
raciones fundamentales para su funcionamiento. Consiste en la combinación de ceros y unos (código
binario) para formar las órdenes interpretables por el hardware de la computadora. Este lenguaje es
mucho más rápido que los lenguajes de alto nivel. La desventaja es que son bastantes difı́ciles de ma-
nejar y usar, además de tener códigos fuente enormes donde encontrar un fallo es casi imposible. El
lenguaje ensamblador es un derivado del lenguaje máquina y esta formado por abreviaturas de letras
y números llamadas mnemotécnicos. Con la aparición de este lenguaje se crearon los programas tra-
ductores para poder pasar los programas escritos en lenguaje ensamblador a lenguaje máquina. Como
ventaja con respecto al código máquina es que los códigos fuentes eran más cortos y los programas
creados ocupaban menos memoria. Las desventajas de este lenguaje siguen siendo prácticamente las
mismas, añadiendo la dificultad de tener que aprender un nuevo lenguaje difı́cil de probar y mantener.

Lenguajes de alto nivel

Son aquellos que se encuentran más cercanos al lenguaje natural que al lenguaje máquina. Están
dirigidos a solucionar problemas mediante el uso de estructuras dinámicas de datos, algo muy utilizado
en todos los lenguajes de programación. Son estructuras que pueden cambiar de tamaño durante la
ejecución del programa. Nos permiten crear estructuras de datos que se adapten a las necesidades reales
de un programa.
Se trata de lenguajes independientes de la arquitectura del ordenador. Por lo que, en principio, un
programa escrito en un lenguaje de alto nivel, puede migrar de una máquina a otra sin ningún tipo de
problema.
Estos lenguajes permiten al programador olvidarse por completo del funcionamiento interno de la
máquina para la que están diseñados. Tan sólo necesitan un traductor que entienda tanto el código
fuente como las caracterı́sticas de la máquina.

4.2.2. Ejecución

Se denominan lenguajes compilados cuando un compilador (programa traductor) traduce el código


fuente del programa en código máquina (código objeto). Otro programa, el enlazador, unirá los ficheros
de código objeto del programa principal con los de las librerı́as para producir el programa ejecutable.
Ejemplos: C, Java.
En contraposición, en los lenguajes interpretados un programa intérprete ejecuta las instrucciones del
programa de manera directa. Ejemplo: Lisp,Perl.
También hay lenguajes mixtos, como Java, que primero pasan por una fase de compilación en la que
el código fuente se transforma en “bytecode”, y éste puede ser ejecutado luego (interpretado) en or-
denadores con distintas arquitecturas (procesadores) que tengan todos instalados la misma “máquina
virtual ” Java.

4.2.3. Paradigma de programación.

El paradigma de programación es el estilo de programación empleado. Algunos lenguajes soportan varios


paradigmas, y otros sólo uno. Se puede decir que históricamente han ido apareciendo para facilitar la
tarea de programar según el tipo de problema a abordar, o para facilitar el mantenimiento del software,
o por otra cuestión similar, por lo que todos corresponden a lenguajes de alto nivel (o nivel medio),
estando los lenguajes ensambladores “atados” a la arquitectura de su procesador correspondiente.
Los lenguajes clásicos de programación procedural dividen el problema en partes más pequeñas, que
serán realizadas por subprogramas (subrutinas, funciones o procedimientos), que se llaman unas a otras
para ser ejecutadas. Ejemplos: C, Pascal.
Otro tipo es la programación orientada a objetos (POO), donde se crea un sistema de clases y objetos
siguiendo el ejemplo del mundo real, en el que unos objetos realizan acciones y se comunican con otros
objetos. Ejemplos: C++, Java. La programación orientada a objetos es una forma de programar que
trata de encontrar una solución a varios problemas. Introduce nuevos conceptos, que superan y amplı́an
conceptos antiguos ya conocidos. Entre ellos destacan los siguientes:
Clase: definiciones de las propiedades y comportamiento de un tipo de objeto concreto. La ins-
tanciación es la lectura de estas definiciones y la creación de un objeto a partir de ellas.
Herencia (por ejemplo, herencia de la clase C a la clase D): es la facilidad mediante la cual la clase
D hereda en ella cada uno de los atributos y operaciones de C, como si esos atributos y operaciones
hubiesen sido definidos por la misma D. Por lo tanto, puede usar los mismos métodos y variables
públicas declaradas en C. Los componentes registrados como “privados” (private) también se
heredan, pero como no pertenecen a la clase, se mantienen escondidos al programador y sólo
pueden ser accedidos a través de otros métodos públicos. Esto es ası́ para mantener hegemónico
el ideal de OOP.
Objeto: entidad provista de un conjunto de propiedades o atributos (datos) y de comportamiento
o funcionalidad (métodos) los mismos que consecuentemente reaccionan a eventos. Se corresponde
con los objetos reales del mundo que nos rodea, o a objetos internos del sistema (del programa).
Es una instancia a una clase.
Método: algoritmo asociado a un objeto (o a una clase de objetos), cuya ejecución se desencadena
tras la recepción de un “mensaje”. Desde el punto de vista del comportamiento, es lo que el
objeto puede hacer. Un método puede producir un cambio en las propiedades del objeto, o la
generación de un “evento” con un nuevo mensaje para otro objeto del sistema.
Modularidad : propiedad que permite subdividir una aplicación en partes más pequeñas (llamadas
módulos), cada una de las cuales debe ser tan independiente como sea posible de la aplicación en
sı́ y de las restantes partes. Estos módulos se pueden compilar por separado, pero tienen cone-
xiones con otros módulos. Al igual que la encapsulación, los lenguajes soportan la modularidad
de diversas formas.
Otros lenguajes de programación funcional realizan tareas evaluando funciones, como en Matemáticas,
de manera recursiva. Ejemplo: Lisp.
La programación lógica consiste en expresar una tarea empleando lógica formal matemática. Ejemplo:
Prolog.
Otros paradigmas de programación son: programación genérica, programación reflexiva, programación
orientada a procesos, etc.

4.2.4. ¿Que lenguaje de programación usar?

La elección del lenguaje de programación nunca deberı́a ser aleatoria o basada únicamente en prefe-
rencias personales. Ya sea más o menos fácil el aprender un determinado lenguaje de programación,
lo ideal es usar el más óptimo dependiendo de la tarea que vayamos a realizar. El rendimiento a la
hora de ejecutar un determinado programa, puede ser uno de los criterios a tener en cuenta. El tiempo
empleado en leer o “parsear ” un fichero (que podrı́amos definir como obtener información a partir de
datos, los cuales aparecen en un determinado formato no enriquecido), por ejemplo en formato FASTA,
o en realizar un alineamiento de secuencias, depende en gran medida del lenguaje de programación a
usar [1]. Lenguajes como C o C++ , son conocidos por su alto rendimiento en cuanto a uso de pro-
cesador como a uso de memoria RAM, sin embargo son lenguajes de alto nivel que a priori sin ser
informáticos, pueden resultar complicados de dominar. En la mayorı́a de situaciones deberemos buscar
un compromiso entre la dificultad y el tiempo a emplear en escribir el programa y el rendimiento del
mismo. No tiene sentido aprender un nuevo lenguaje para ahorrar una hora de cálculo. Si tenemos
en cuenta el estudio sobre los lenguajes más usados en el campo de la bioinformática en el año 2012,
mostrado en la Figura 4.1, podemos comprobar como R (lenguaje de programación para el análisis
estadı́stico) junto a Perl y Python, son los más usados.

Figura 4.1: Porcetanje de uso de lenguajes de programación en 2012.

Un buen bioinformático, deberı́a ser capaz de programar en varios lenguajes y usar uno u otro en
función de sus necesidades y los recursos computacionales a su disposición, ası́ como los algoritmos a
usar y el tipo de datos de partida. En el presente texto escogeremos Perl como modelo de lenguaje de
programación de alto nivel para explicar su funcionamiento a lo largo del capı́tulo. Veremos los tipos
de datos con los que trabaja, esencialmente escalares, arrays y hashes. A su vez veremos estructuras
de control, como bucles y sentencias condicionales y expresiones regulares entre otros.

4.3. Consola de Linux, Mac OS X y Windows. Operaciones básicas

Mirando al pasado de la informática, la forma de interactuar con los ordenadores de la época era
con una consola fı́sica (Figura 4.2). Dicha consola fı́sica era un teclado unido a una pantalla, que
estaba conectado por puerto serie al ordenador. Los contenidos que era capaz de mostrar esa consola
eran únicamente textuales y monocromáticos, sin ninguna capacidad gráfica. Toda interacción con el
ordenador era a través de esa consola, tecleando órdenes y leyendo la salida proporcionada por esas
órdenes.

Figura 4.2: Terminal DEC VT100, año 1978.

Esta forma de trabajo, aunque en principio parece arcaica, sigue vigente, debido a que es mucho más
sencillo realizar el mismo conjunto de operaciones sobre muchos ficheros desde la consola que desde
el entorno gráfico. Dicho conjunto de operaciones se pueden automatizar mediante la preparación de
un lote de órdenes (o script), que es un guión de operaciones que dicta qué operaciones se realizan
sobre los ficheros, y en qué orden. Actualmente, casi todos los sistemas operativos y entornos gráficos
proporcionan programas de consola virtual que permiten seguir con esta forma de trabajo.

4.3.1. Tipos de Shell

Una consola de texto (ya sea fı́sica o virtual) no sirve de nada sin un intérprete de órdenes para la lı́nea
de comandos (o shell ). Una shell es un programa que entiende una serie de órdenes textuales comunes,
como por ejemplo cambiar de directorio, mostrar los contenidos de un directorio, copiar, mover y borrar
ficheros y directorios, y ejecutar programas.
En los sistemas Windows, la shell usada se llama ‘cmd’, mientras que para los sistemas derivados
de UNIX, como por ejemplo Linux o Mac OS X, las shells que se usan principalmente son ‘bash’ y
‘tcsh’. Todas estas shells disponen de su propio lenguaje de script, que permite no sólo ejecutar una
serie de órdenes una detrás de otra, sino también operaciones más complejas como aplicar las mismas
operaciones a cada uno de los ficheros de un directorio, y manipular variables de entorno. Las variables
de entorno rigen, entre otras cosas, dónde buscar los programas a ser ejecutados en la shell. La Tabla 4.1
muestra algunas de las operaciones básicas que se pueden realizar en una shell.

Operación básica cmd bash tcsh


Listado de variables de en- SET env, printenv
torno
Valor de una variable de ECHO %VARIABLE % echo $VARIABLE
entorno
Establecer una variable de SET export setenv VARIABLE
entorno VARIABLE="valor" VARIABLE="valor" "valor"
Listado del directorio ac- DIR /W ls
tual
Contenidos de un directo- DIR /W directorio ls directorio
rio
Contenido detallado DIR directorio ls -l directorio
Directorio actual ECHO %CD % pwd
Cambiar de directorio XS directorio cd directorio
Crear un directorio MKDIR directorio mkdir directorio
Copiar un fichero COPY fichero destino cp fichero destino
Copiar un directorio XCOPY /T /E cp -r directorio destino
directorio destino
Borrar un fichero DEL fichero rm fichero
Borrar un directorio RD /S directorio rm -r directorio
Renombrar un fichero o di- REN nombreViejo mv nombreViejo nombreNuevo
rectorio nombreNuevo
Mover un fichero o directo- MOVE nombreViejo mv nombreViejo destino
rio destino

Tabla 4.1: Operaciones básicas que se pueden ejecutar en una shell.

4.4. Perl y BioPerl

4.4.1. Introducción a Perl

Perl es el acrónimo de “lenguaje práctico para la extracción y el informe”(Practical Extraction and


Report Language). Perl es un lenguaje de programación que toma caracterı́sticas del lenguaje C, del
lenguaje interpretado bourne shell (‘sh’), AWK, sed, Lisp y, en un grado inferior, de muchos otros
lenguajes de programación. Estructuralmente, Perl está basado en un estilo de bloques como los de C
o AWK y fue ampliamente adoptado por su rendimiento en el procesado de texto. 1
Fue diseñado por Larry Wall en 1987 y hasta 1991 la única documentación de Perl era una simple (y
cada vez más larga) página de manual Unix. En 1991 se publicó Programming Perl [2] y se convirtió en
la referencia de facto del lenguaje. Al mismo tiempo, el número de versión de Perl saltó a 4, no por
marcar un gran cambio en el lenguaje, sino por identificar a la versión que estaba documentada en el
libro. En este punto, Larry Wall abandonó Perl 4 para comenzar a trabajar en Perl 5, cuyo desarrollo
continuó hasta 1994. Perl 5 fue publicado el 17 de octubre de 1994. Fue casi una completa reescritura del
intérprete y añadió muchas nuevas caracterı́sticas al lenguaje, incluyendo objetos, referencias, paquetes
y módulos. A destacar, los módulos proveen de un mecanismo para extender el lenguaje sin modificar
el intérprete. Esto permitió estabilizar su núcleo principal, además de permitir a los programadores de
Perl añadirle nuevas caracterı́sticas. El 26 de octubre de 1995, se creó el Comprehensive Perl Archive
Network 2 (CPAN). CPAN es una colección de sitios web que almacenan y distribuyen fuentes en Perl,
binarios, documentación, scripts y módulos. En 2012, Perl 5 es la versión más popular y continúa
siendo mantenido. La nueva versión 6 de Perl 6 comenzó ser desarrollada en 2000 y todavı́a no ha sido
terminada.
Perl está implementado como un intérprete de comandos, escrito en C, junto con una gran colección
de módulos, escritos en Perl y C. El intérprete tiene una arquitectura orientada a objetos. Todos los
elementos del lenguaje Perl —escalares, listas, hashes, referencias a código, manejadores de archivo—
están representados en el intérprete como estructuras C. Las operaciones sobre estas estructuras están
definidas como una numerosa colección de macros, typedef y funciones; estos constituyen la API C de
1
Perl en Wikipedia. http://es.wikipedia.org/wiki/Perl
2
Comprehensive Perl Archive Network. http://www.cpan.org
Perl. La API Perl puede ser desconcertante para el no iniciado, pero sus puntos de entrada siguen un
esquema de nombres coherente, que ayuda a los que quieran utilizarla. La ejecución de un programa Perl
se puede dividir en dos fases: tiempo de compilación y tiempo de ejecución. En tiempo de compilación
el intérprete parsea el texto del programa en un árbol sintáctico. En tiempo de ejecución, ejecuta el
programa siguiendo el árbol. El texto es parseado sólo una vez y el árbol sintáctico es optimizado
antes de ser ejecutado, para que la fase de ejecución sea relativamente eficiente. Perl está diseñado
para realizar tareas sencillas que con una shell serı́a complicado de llevar a cabo y facilita su escritura,
comparada con otros lenguajes, como C.
Perl es software libre y está licenciado bajo la GNU General Public License y la Artistic License. Existen
distribuciones disponibles para la mayorı́a de sistemas operativos. Está especialmente extendido en Unix
y en sistemas similares, pero ha sido portado a las plataformas más modernas (y otras más obsoletas).
Puede ser compilado desde el código fuente en todos los Unix, compatibles POSIX o cualquier otra
plataforma Unix compatible. Sin embargo, esto no es normalmente necesario, porque Perl está incluido
por defecto en la instalación de los sistemas operativos más populares a excepción de Windows.

4.4.2. Instalación

Instalación Linux

Perl está instalado por defecto en las distribuciones más populares de GNU/Linux incluyendo Gentoo,
Slackware, Mandriva, Debian, RedHat, SUSE y Ubuntu. En el caso de que quisiéramos instalarlo,
tendrı́amos dos opciones:
1. Usar binarios precompilados. Un binario precompilado no es mas que el intérprete de perl, ası́ co-
mo las librerı́as y los módulos necesarios para que funcione. Su instalación implica descargar el
archivo, descomprimirlo y ejecutarlo desde la propia página de Perl3 o la versión ofrecida por la
empresa ActiveState4 .
2. Compilar perl desde el código fuente. El código fuente, no es más que el código escrito direc-
tamente por el autor, para que nosotros creemos nuestros propios binarios. Las instrucciones a
ejecutar en consola para instalar la versión 5.14 son las siguientes :
$ wget http://www.cpan.org/src/5.0/perl-5.14.2.tar.gz
$ tar -xzf perl-5.14.2.tar.gz
$ cd perl-5.14.2
$ ./Configure -des -Dprefix=$HOME/localperl
$ make
$ make test
$ make install

Instalación en MacOS

En el caso de los sistemas operativos de Apple, Perl viene incluido por defecto y no es necesario
hacer nada especial. Simplemente abrir la consola y empezar a programar. En el caso de necesitar
una instalación propia, ésta instalación, ya sea a través de binarios o del código fuente, es igual a la
explicada anteriormente para los ordenadores que usan Linux. Después de todo, ambos proceden del
sistema operativo UNIX.
3
Página oficial de Perl. http://www.perl.org
4
ActiveState Perl. http://www.activestate.com
Instalación en Windows

Los usuarios de Windows normalmente instalan una distribución binaria de Perl. Compilar Perl desde el
código fuente bajo Windows es posible, pero la mayorı́a de las instalaciones no disponen del necesario
compilador de C. La opción más sencilla es descargar un fichero ejecutable desde la página web de
ActiveState4 que nos instalará todo lo necesario para que Perl funcione desde el interfaz de comandos
propio de Windows. El sistema de emulación Cygwin5 también proporciona otra forma de ejecutar Perl
bajo Windows. Cygwin proporciona en entorno parecido al Unix en Windows que incluye ‘gcc’, por lo
que compilar Perl desde el código es una opción accesible para los usuarios que prefieren esta opción.

4.4.3. Programando en Perl

A lo largo de esta sección, iremos profundizando en los detalles más relevantes de Perl. Empezando por
el programa más sencillo. En Perl, el programa canónico ‘Hola mundo’ es:
#!/usr/bin/perl
print "Hola mundo\n"; # Imprime "Hola mundo"

La primera lı́nea contiene el “shebang” (par de caracteres que identifica el texto que sigue), que le
indica al sistema operativo dónde encontrar el intérprete de Perl. La segunda imprime el string “Hola
mundo” y un carácter de nueva lı́nea. Todos los comandos de Perl terminan en punto y coma (‘;’). Se
pueden insertar comentarios en el código con el sı́mbolo almohadilla (‘#’), todo lo que siga a dicho
sı́mbolo no será leı́do al ejecutar el código. El shebang es la forma normal para invocar al intérprete
en los sistemas Unix. Los sistemas Windows pueden seguir utilizándolo o pueden asociar la extensión
de archivo ‘.pl’ con el intérprete Perl. Algunos editores de texto también usan la lı́nea shebang como
una pista sobre el modo de trabajo en que deben operar. Si el programa es ejecutado por Perl y no
invocado por el shell, la lı́nea que empieza por el shebang es parseada para interpretar las opciones. En
otro caso, es ignorada.

Tipos de datos en perl.

Perl tiene tres tipos de datos: escalares, listas y hashes:


Un escalar es un solo valor; puede ser un número, un string (cadena de caracteres) o una refe-
rencia.
Una lista es una colección ordenada de escalares (una variable que almacena una lista se llama
array).
Un hash, o memoria asociativa, es un mapeo de strings a escalares; los strings se llaman claves y
los escalares valores.
Todas las variables están precedidas por un sigilo, que identifica el tipo de dato que es accedido (no el
tipo de dato de la misma variable). Se puede usar el mismo nombre para variables de diferentes tipos
sin que tengan conflictos. Los números se escriben de la forma usual, los strings (texto) están rodeados
entre comillas, ya sean dobles o simple. Ver ejemplos de tipos de datos en el ??.
$variable; # un escalar
@array; # un array
%hash; # un hash
$n = 12;
5
Cygwin project. http://www.cygwin.com
$nombre = "coche";
$color = 'rojo';

Una caracterı́stica de Perl es que reconoce el tipo de dato de una variable. Perl convertirá los strings
en números y viceversa dependiendo del contexto en que sean usados. En el siguiente ejemplo (??) los
strings ‘$n’ y ‘$m’ son tratados como números cuando son argumentos del operador suma. Este código
imprime el número ‘5’, desechando cualquier información no numérica de la operación y dejando los
valores de las variables intactos.
$n = "3 casas";
$m = "20 motos";
print $n + $m; # Resultado '5'

Una lista se define listando sus elementos, separados por comas y rodeados por paréntesis donde ası́ sea
requerido por la precedencia de los operadores:
@numeros = (32, 45, 16, 5);

Un hash puede ser inicializado desde una lista de pares clave/valor:


%color = (
coche => 'rojo',
moto => 'azul',
);

Los elementos individuales de una lista son accedidos utilizando un ı́ndice numérico, dentro de corchetes.
Valores individuales en un hash son accedidos utilizando la correspondiente clave, dentro de llaves. El
sı́mbolo ‘$’ identifica que el elemento accedido es un escalar.
$numeros[2];
$color{'moto'};

Múltiples elementos pueden ser accedidos usando en su lugar el sigilo ‘@’ (identificando el resultado
como una lista).
@numeros[2, 3, 1] # tres elementos de @puntuaciones
@color{'coche', 'moto'} # dos valores de %color

El número de elementos en un array puede ser obtenido mediante la función ‘scalar’ o con la ayuda
del sigilo ‘$#’ éste último da el ı́ndice del último elemento dentro del array, que será una unidad menor
que el número de elementos puesto que el ı́ndice del primer elemento del array es ‘0’.
scalar @amigos; # número de elementos en @amigos
$#amigos; # ı́ndice del último elemento

Hay unas pocas funciones que operan sobre hashes enteros:


@nombres_de_clientes = keys %direcciones; # guarda todas las claves
@direcciones_de_email = values %direcciones; # guarda todos los valores

Estructuras de control

Perl tiene varias clases de estructuras de control, estructuras de control orientado al bloque o bucles,
similar a los de los lenguajes de programación C y Java. Y estructuras condicionales. Las condiciones
están rodeadas por paréntesis y los bloques subordinados por llaves:
while ( condición ) {... }
while ( condición ) {... } continue {... }
for ( expresión inicial; expresión condicional; expresión incremental ) {... }
foreach var ( lista ) {... }
foreach var ( lista ) {... } continue {... }
if ( condición ) {... }
if ( condición ) {... } else {... }
if ( condición ) {... } elsif ( condición ) {... } else {... }

Las palabras clave de control de flujo ‘next’, ‘last’, ‘return’ y ‘redo’ son expresiones, por lo que
pueden ser usadas con los operadores cortocircuito. Perl también tiene dos construcciones implı́citas
para bucles:
resultados = grep {... } lista
resultados = map {... } lista

‘grep’ devuelve todos los elementos de lista en que el bloque subordinado evalúa a verdadero. ‘map’
evalúa el bloque subordinado por cada elemento de lista y devuelve una lista de los valores resultantes.
Estas construcciones permiten un estilo simple de programación funcional. No hay declaración ‘switch’
(salto multi-camino) en Perl 5. La documentación Perl describe media docena de formas de conseguir
el mismo efecto usando otras estructuras de control. Existe sin embargo un módulo Switch, que
proporciona la funcionalidad modelada para el próximo Perl 6. Perl incluye una declaración ‘goto
¡etiqueta¿’, pero es usada raramente. Las situaciones donde en otros lenguajes se utiliza ‘goto’ no
ocurren tan a menudo en Perl debido a sus amplias opciones de control de flujo. Existe también una
declaración ‘goto &sub’ que realiza una llamada final. Termina la subrutina actual e inmediatamente
llama a la subrutina especificada. Esto se usa en situaciones donde una nueva subrutina puede realizar
una gestión de la pila más eficiente que el propio Perl (porque tı́picamente no se requiere ningún cambio
en la pila actual), y en una recursión muy profunda este tipo de llamadas puede tener un sustancial
impacto positivo en el funcionamiento porque evita la sobrecarga de la gestión contexto/pila en el
momento de retornar.

Subrutinas

Las subrutinas se definen con la palabra clave ‘sub’ e invocadas simplemente nombrándolas. Si la
subrutina en cuestión no ha sido todavı́a declarada, es necesario, para el proceso de análisis sintáctico,
poner los paréntesis.
foo(); # paréntesis necesarios aquı́...
sub foo {... }
foo; # pero no aquı́

Una lista de argumentos pueden ser indicados después del nombre de la subrutina. Los argumentos
pueden ser escalares, listas o hashes.
foo($x, @y, %z);

Los parámetros de una subrutina no necesitan ser declarados, ni en número ni en tipo, de hecho, pueden
variar en cada llamada. Los arrays son expandidos a sus elementos, los hashes a una lista de pares
clave/valor y todo el conjunto es pasado a la subrutina como una indiferenciada lista de escalares.
Cualesquiera de los argumentos pasados están disponibles para la subrutina en el array especial ‘@ ’.
Los elementos de ‘@ ’ son asociados a los argumentos actuales; cambiando un elemento de ‘@ ’ cambia el
argumento correspondiente. Los elementos de ‘@ ’ pueden ser accedidos con los subı́ndices de la forma
normal:
$_[0], $_[1]

Sin embargo, el código resultante puede ser difı́cil de leer y los parámetros tener una semántica de pase
por referencia, que puede resultar algo no deseable. Un modismo común es asignar ‘@ ’ a una lista de
variables con nombres:
my($x, $y, $z) = @_;
Esto afecta tanto a la mnemónica de los nombres de los parámetros como a la semántica de los valores
pasados por valor. La palabra clave ‘my’ indica que las siguientes variables están léxicamente embebidas
en el bloque que las contienen. Otro modismo es sacar los parámetros de ‘@ ’. Esto es muy común cuando
la subrutina toma un sólo argumento:
my $x = shift; # Si no se dice nada, nos referimos a @_

Las subrutinas pueden devolver valores:


return 42, $x, @y, %z;

Si la subrutina no sale vı́a declaración ‘return’, entonces devuelve la última expresión evaluada en el
cuerpo de la subrutina. Arrays y hashes en el valor de retorno son expandidos a una lista de escalares,
igual que si fueran argumentos de una función. La expresión devuelta es evaluada en el contexto de la
llamada de la subrutina; esto puede sorprender al desprevenido.
sub lista { (4, 5, 6) }
sub array { @x = (4, 5, 6); @x }

$x = lista; # devuelve 6 - último elemento de la lista


$x = array; # devuelve 3 - número de elementos de la lista
@x = lista; # devuelve (4, 5, 6)
@x = array; # devuelve (4, 5, 6)

Expresiones regulares con Perl

El lenguaje Perl incluye una sintaxis especializada para escribir expresiones regulares y el intérprete
contiene un motor para emparejar strings con expresiones regulares. El motor de expresiones regulares
usa un algoritmo de vuelta atrás (backtracking), extendiendo sus capacidades desde el simple empare-
jamiento de patrones simples con la captura y sustitución de strings. El motor de expresiones regulares
se deriva de ‘regex’, escrito por Henry Spencer. La sintaxis de expresiones regulares fue originalmente
tomada de las expresiones regulares de Unix 8. Sin embargo, se diferenció ya antes del primer lan-
zamiento de Perl y desde entonces ha ido incorporando muchas más caracterı́sticas. Otros lenguajes
y aplicaciones están adoptando las expresiones regulares de Perl denominadas PCRE en vez de las
expresiones regulares POSIX, incluyendo PHP, Ruby, Java y el Servidor HTTP Apache. El operador
‘m//’ (empareja) permite comprobar un emparejamiento por medio de una expresión regular (para
abreviar, el precedente ‘m’ puede ser omitido). En el caso más simple, una expresión como:
$x = ∼ m/abc/;

evalúa a verdadero si y sólo si el string ‘$x’ empareja con la expresión regular ‘abc’. Partes de la
expresión regular pueden ser incluidas entre paréntesis y las partes correspondientes de un string
emparejado son capturadas. Los strings capturados son asignados de forma secuencial a las variables
internas ‘$1’, ‘$2’, ‘$3’,... y una lista de strings capturados se devuelve como valor del emparejamiento:
$x = ∼ m/a(.)c/; # captura el carácter entre 'a' y 'c' y lo guarda en $1

El operador ‘s///’ (sustitución) especifica una operación de búsqueda y reemplazo:


$x = ∼ s/abc/aBc/; # Convierte la `b' en mayúscula

Las expresiones regulares en Perl pueden tomar unos modificadores. Son sufijos de una sola letra que
modifican el significado de la expresión:
$x = ∼ m/abc/i; # emparejamiento independientemente de si están en mayúscula o min
úscula
$x = ∼ s/abc/aBc/g; # búsqueda y reemplazo recursivo global (a lo largo de todo el
string)
Las expresiones regulares pueden ser densas y crı́pticas. Esto es porque la sintaxis de las expresiones
regulares es extremadamente compacta, generalmente usando caracteres sueltos o pares de caracteres
que representan sus operaciones.
Un uso común de las expresiones regulares es el de especificar delimitadores de campos al operador
‘split’:
@palabras = split m/,/, $linea; # divide $lı́nea en valores separados por comas.

El operador ‘split’ complementa la captura de string. La captura de string devuelve las partes que
emparejan con una expresión regular y ‘split’ devuelve las partes que no emparejan.

Ejemplos simples

Ejemplo 1. Cómo convertir un secuencia de DNA en RNA (Código 4.1). Como sabéis, el DNA está com-
puesto de 4 tipo de nucleótidos denominados, adenina (A), guanina (G), citosina (C) y timina (T)
(Sección 7.3). En el caso del RNA la base equivalente a la timina (T) es el uracilo (U).

Código 4.1: Traduciendo DNA en RNA.


1 #!/usr/bin/perl
2 #Nuestra secuencia de DNA
3 $secuencia_DNA="AGCTTGACGTGACACATG";
4 #Como RNA es igual al DNA (a excepción del uracilo), copiamos el valor
5 $secuencia_RNA=$secuencia_DNA;
6 #Ahora cambiamos las timinas (T) por uracilos (U) a lo largo de todo el string
7 $secuencia_RNA = ∼ s/T/U/g;
8 #Imprimimos el resultado
9 print "La secuencia de DNA '$secuencia_DNA' transformada en RNA es '$secuencia_RNA'\n";

El resultado debe ser:


La secuencia de DNA 'AGCTTGACGTGACACATG' transformada en RNA es
'AGCUUGACGUGACACAUG'.

Ejemplo 2. Cómo convertir un secuencia de RNA en un fichero en DNA (Código 4.2). En este caso,
vamos a hacer el caso contrario, es decir convertir una secuencia de RNA en DNA. La complicación
viene por el hecho de leer la secuencia directamente desde un fichero en nuestro ordenador. Lo primero
que necesitamos es un fichero con una secuencia de RNA. Por lo tanto usando un editor de texto,
creamos un fichero llamado ‘secuenciaRNA.txt’, cuyo contenido sea el siguiente:
UUACGGCGGAGCUUUGACGCUACGAUCGAUCG

Código 4.2: Traduciendo RNA de un fichero en DNA.


1 #!/usr/bin/perl
2 #Pedimos al usuario que introduzca la ruta al fichero con la secuencia
3 print "Introduce el fichero con tu secuencia: ";
4 #El parámetro introducido (la ruta al fichero), se asocia a la variable $fichero_RNA.
SDTIN = Standard Input
5 $fichero_RNA = <STDIN>;
6 #Al escribir la ruta, presionamos la tecla Intro, la cual no forma parte del nombre del
fichero. Por eso se elimina con la funcion chomp.
7 chomp $fichero_RNA;
8 #Abrimos el fichero cuyo contenido será una secuencia. Si no se puede abrir, el programa
muere dando un error.
9 open(RNAFILE, $fichero_RNA) || die "No puedo abrir el fichero ($fichero_RNA)\n";
10 #Asociamos a la variable RNA_secuencia, el contenido de fichero $fichero_RNA
11 $secuencia_RNA = <RNAFILE>;
12 #Como RNA es igual al DNA (a excepción del uracilo), copiamos el valor.
13 $secuencia_RNA=$secuencia_DNA;
14 #Ahora cambiamos las timinas por uracilos a lo largo de todo el string.
15 $secuencia_RNA = ∼ s/T/U/g;
16 #Imprimimos el resultado
17 print "La secuencia de DNA '$secuencia_DNA' transformada en RNA es '$secuencia_RNA'\n";

El resultado debe ser :


Introduce el fichero con tu secuencia: secuenciaRNA.txt
La secuencia de RNA 'UUACGGCGGAGCUUUGACGCUACGAUCGAUCG' trasformada en DNA es
'TTACGGCGGAGCTTTGACGCTACGATCGATCG'

Ejemplo 3. Cómo obtener la secuencia complementaria a una secuencia de DNA (Código 4.3). Lo
primero que necesitamos es de nuevo un fichero con una secuencia de DNA. Por lo tanto usando un
editor de texto, creamos un fichero llamado ‘secuenciaDNA.txt’, cuyo contenido sea el siguiente:
TGTGGTACGTACGACAAACGTCGTACGTA

Código 4.3: Secuencia reversa complementaria.


1 #!/usr/bin/perl
2 #Pedimos al usuario que introduzca la ruta al fichero con la secuencia
3 print "Introduce el fichero con tu secuencia: ";
4 #El parámetro introducido (la ruta al fichero), se asocia a la variable $fichero_DNA.
SDTIN = Standard Input
5 $fichero_DNA = <STDIN>;
6 #Al escribir la ruta, presionamos la tecla Intro, la cual no forma parte del nombre del
fichero. Por eso se elimina con la funcion chomp.
7 chomp $fichero_DNA;
8 #Abrimos el fichero cuyo contenido será una secuencia. Si no se puede abrir, el programa
muere dando un error.
9 open(DNAFILE, $fichero_DNA) || die "No puedo abrir el fichero ($fichero_DNA)\n";
10 #Asociamos a la variable $DNA, el contenido de fichero $fichero_DNA
11 $DNA = <DNAFILE>;
12 #Creamos un array @nucleotidos, de forma que en cada posición del array, haya un nucle
ótido procedente de $DNA
13 @nucleotidos=split(//,$DNA);
14 #Definimos un hash para hacer las conversiones en la hebra contraria a la nuestra.
15 my %hash_complementarias;
16 $hash{a}="t";
17 $hash{c}="g";
18 $hash{g}="c";
19 $hash{t}="a";
20 $hash{u}="a";
21 #Usamos un bucle para recorrer de atrás hacia adelante la secuencia. Ası́ la obtenemos de
forma inversa
22 print "La secuencia reversa complemetaria de DNA '$DNA' es '";
23 for ($var=$#nucleotidos; $var>=0; $var--) {
24 #Cada posición del array @nucleotidos, por ejemplo $nucleotidos[5].
25 #Pero como lo que queremos es su complementario, el nucleótido resultado se obtiene
26 #usando nuestro hash de secuencias complementarias
27 print $hash{$nucleotidos[$var]};
28 }
29 print "'\n";

El resultado debe ser:


La secuencia reversa complemetaria del DNA 'TGTGGTACGTACGACAAACGTCGTACGTA' es
'ATGCATGCTGCAAACAGCATGCATGGTGT'.
Ejemplo 4. Cómo obtener la longitud de una secuencia de nucleótidos (Código 4.4). En este último
ejemplo haremos uso de una subrutina llamada ‘contador’ que tomará como valor de entrada una
variable con la secuencia y retornará como salida su longitud. Usaremos la misma secuencia creada en
el ejemplo anterior en el fichero llamado ‘secuenciaDNA.txt’.

Código 4.4: Longitud de una secuencia.


1 #!/usr/bin/perl
2 #Pedimos al usuario que introduzca la ruta al fichero con la secuencia
3 print "Introduce el fichero con tu secuencia: ";
4 #El parámetro introducido (la ruta al fichero), se asocia a la variable
$fichero_sequencia. SDTIN = Standard Input
5 $fichero_secuencia = <STDIN>;
6 #Al escribir la ruta, presionamos la tecla Intro, la cual no forma parte
7 #del nombre del fichero. Por eso se elimina con la función 'chomp'.
8 chomp $fichero_secuencia;
9 #Abrimos el fichero cuyo contenido será una secuencia. Si no se puede abrir, el programa
muere dando un error.
10 open(SEQFILE, $fichero_secuencia) || die "No puedo abrir el fichero ($fichero_secuencia)
\n";
11 #Asociamos a la variable $secuencia, el contenido de fichero $fichero_secuencia
12 $secuencia = <SEQFILE>;
13 #Enviamos la secuencia a una subrutina que nos devolverá el número de elementos
14 $longitud_secuencia=contador($secuencia);
15 print "El tamaño de la secuencia '$secuencia' es: $longitud_secuencia\n";
16 #Subrutina para contar los elementos
17 sub contador(){
18 #Usamos el my, para que la variable $secuencia sólo exista dentro de al subrutina y no
interfiera con la usada anteriormente
19 my $secuencia=shift;
20 #Separamos el string de la secuencia en cada uno de sus elementos
21 @split=split(//,$secuencia);
22 #devolvemos el número total de elementos
23 return(scalar(@split));
24 }

Al usar éste programa con el fichero usado anteriormente, el resultado es el siguiente:


Introduce el fichero con tu secuencia: secuenciaDNA.txt
El tamaño de la secuencia 'TGTGGTACGTACGACAAACGTCGTACGTA' es: 29

4.4.4. BioPerl y la programación orientada a objetos

¿Que es BioPerl?

BioPerl es una colección de módulos de Perl que facilitan el desarrollo de scripts en Perl para apli-
caciones de bioinformática [3]. Ha desempeñado un papel integral en el Proyecto Genoma Humano.6
Se trata de un activo proyecto de software libre apoyado por la Open Bioinformatics Foundation. La
primera versión estable fue lanzada el 11 de junio de 2002; la última estable (en términos de la API)
es la 1.6.9, lanzada en abril de 2011. La versión 1.6.0 es considerada la más estable (en términos de
errores) y la recomendada para el uso diario. También hay lanzamientos periódicos producidos por
desarrolladores. Con el fin de aprovechar BioPerl, el usuario necesita una comprensión básica del len-
guaje de programación Perl que incluya una comprensión de cómo Perl utiliza las referencias, módulos,
objetos y métodos.
6
BioPerl en Wikipedia. http://es.wikipedia.org/wiki/BioPerl
Instalación de BioPerl

Instalación en Linux/MacOs. La instalación que recomendamos en los sistemas basados en Unix,


es la basada en CPAN. Como se explicó anteriormente (Subsección 4.4.1), en CPAN podemos encontrar
multitud de paquetes que nos facilitarán mucho a la hora de programar. Siguiendo las instrucciones de
BioPerl [3], los pasos son los siguientes: Desde la consola ejecutamos:
$ perl -MCPAN -e shell
cpan> force install C/CJ/CJFIELDS/BioPerl-1.6.0.tar.gz

Aceptando las opciones por defecto, la instalación se realizará sin problemas.

Instalación en Windows. En el caso de Windows, la instalación es más compleja, ya que carecemos


del compilador de C. La opción más recomendada es instalar la versión de Perl de ActiveState (Sec-
ción 4.4.2) y ejecutar el administrador de paquetes de Perl (Perl Package Manager ) desde la consola:
C:> ppm-shell
ppm>
ppm> install PPM-Repositories
ppm> repo add http://bioperl.org/DIST
ppm> repo add uwinnipeg
ppm> repo add trouchelle
ppm> install BioPerl

4.4.5. Programando con BioPerl

En la introducción de este capı́tulo se explicó lo que es una clase, un objeto y una función (Subsec-
ción 4.2.3), ahora en este apartado lo veremos con ejemplos. Para ellos vamos a usar la clase secuencia
‘Bio::Seq’, para crear un objeto que contenga nuestra secuencia de interés. Una vez creado, usaremos
varias funciones de BioPerl para procesar la secuencia.
$seq = Bio::Seq->new(
-seq => 'ATGGGGGTGGTGGTACCCT',
-id => 'mi_secuencia');

Podemos trabajar con el objeto secuencia usando métodos propios de BioPerl. En el siguiente ejemplo,
reescribiremos el Código 4.3 usando métodos de BioPerl.

Código 4.5: Secuencia reversa complementaria con BioPerl.


1 #!/usr/bin/perl
2 #Cargamos las clase Seq de BioPerl. Seq es la clase cuyos funciones nos permite trabajar
con secuencias
3 use Bio::Seq;
4 #creamos un objeto secuencia, con su secuencia y su nombre.
5 $seq = Bio::Seq->new(
6 -seq => 'TGTGGTACGTACGACAAACGTCGTACGTA',
7 -id => 'mi_secuencia');
8 #usamos la funcion revcom para obtener la reversa complementaria
9 $reversa_complementaria=$seq->revcom();
10 #Una vez tenemos el objeto con la reversa complementaria, usamos la función seq
11 #para obtener una string con la secuencia.
12 $secuencia_revcomp= $reversa_complementaria->seq();
13 print "La secuencia reversa complemetaria de DNA '" . $seq->seq() . "' es '
$secuencia_revcomp'\n";

El resultado, al igual que el código previo es:


La secuencia complemetaria de DNA: 'TGTGGTACGTACGACAAACGTCGTACGTA' es
'TACGTACGACGTTTGTCGTACGTACCACA'.

En el ejemplo anterior, nosotros mismos tuvimos que programar un bucle inverso para recorrer la
secuencia de forma reversa y para obtener los nucleótidos complementarios creamos un hash con las
equivalencias. En el caso de BioPerl, sólo tenemos que aplicar el método ‘revcom’ a nuestro objeto
secuencia y ya lo tenemos.

4.4.6. Ejercicios con BioPerl

Ejemplo 1. El primer ejemplo, nos servirá para crear la secuencia de proteı́na a partir de un secuencia
de DNA (Código 4.6).

Código 4.6: Traduciendo DNA a proteı́na


1 #!/usr/bin/perl
2 use Bio::PrimarySeq;
3 #Creamos un objecto seceuncia
4 my $seq = Bio::PrimarySeq->new(
5 -seq =>'AACCCTAGCACTGCGCCGAAATATGGCATCCGTGGTATCCCGACTCTGCTGCTGTTCAAAAA',
6 -primary_id => 'Secuencia'
7 );
8 #Usamos el método 'translate', que retorna un objeto secuencia de animoácidos
9 #y a su vez el método 'seq', para que directamente me imprima el resultado.
10 print $seq->translate()->seq() . "\n";

Ejemplo 2. El segundo ejemplo, lo complicaremos un poco más y va a constar de un análisis de


enzimas de restricción sobre una secuencia (Código 4.7). De forma que veremos si existe alguna enzima
capaz de cortar en nuestra secuencia de estudio. Obtendremos las enzimas y los fragmentos cortados.

Código 4.7: Análisis de enzimas de restricción.


1 #!/usr/bin/perl
2 use Bio::Restriction::EnzymeCollection;
3 use Bio::Restriction::Analysis;
4
5 # Creamos un objecto secuencia
6 my $seq = Bio::PrimarySeq->new(
7 -seq =>'AGCTTAATTCATTAGCTCTGACTGCAACGGGCAATATGTCTC',
8 -primary_id => 'Secuencia'
9 );
10 #creamos un objeto análisis de restricción cuyo parámetro es mi secuencia
11 my $ra = Bio::Restriction::Analysis->new(-seq=>$seq);
12 #obtenemos todas las enzimas de restricción capaces de cortar a nuestra secuencia
13 my $all_cutters = $ra->cutters;
14 #Por cada enzima que puede cortar, obtenemos su nombre y la secuencia donde corta
15 foreach my $enz ( $all_cutters->each_enzyme ) {
16 #con el método fragments() obtenemos los fragmentos cortados
17 my @fragments = $ra->fragments($enz);
18 #recorremos los fragmentos (si hay repetición de secuencia se obtendrı́a más de un
corte para la misma enzima, por eso nos devuelve un array)
19 foreach $frag (@fragments){
20 #imprimimos el nombre (con el método 'name') y el fragmento
21 print $enz->name ."\t" . $frag ."\n";
22 }
23 }
4.4.7. Resumen

Como ya hemos comentado a lo largo del capı́tulo, Perl es un lenguaje maduro y ampliamente utilizado
en bioinformática. Es fácil de aprender y su sintaxis es similar al lenguaje inglés hablado, lo que le hace
especialmente útil para aquellos con poca experiencia en el mundo de la programación.
De Perl podemos destacar principalmente: la enorme utilidad de las expresiones regulares (PCRE)
(Sección 4.4.3) y el repositorio CPAN (Subsección 4.4.1) que contiene infinidad de módulos y funciones
que completan al lenguaje y nos ayudan a la hora de realizar casi cualquier tarea, incluyendo BioPerl
(Subsección 4.4.4) para la realización de numerosas tareas bioinformáticas.
4.5. Bibliografı́a
[1] M. Fourment and M. R. Gillings. A comparison of common programming languages used in bioinformatics., volume 9.
2008.
[2] R. L. Schwartz and L. Wall. Programming Perl. jan 1991.
[3] J. E. Stajich, D. Block, K. Boulez, S. E. Brenner, S. A. Chervitz, C. Dagdigian, G. Fuellen, J. G. R. Gilbert, I. Korf,
H. Lapp, H. Lehväslaiho, C. Matsalla, C. J. Mungall, B. I. Osborne, M. R. Pocock, P. Schattner, M. Senger, L. D.
Stein, E. Stupka, M. D. Wilkinson, and E. Birney. The bioperl toolkit: Perl modules for the life sciences. Genome
Research, 12(10):1611–1618, oct 2002.

También podría gustarte