Motywy i petle id 308705 Nieznany

background image

Motywy i pętle

Strona 1

Motywy i pętle

Bardzo często zdarza się, że chcemy odnaleźć w sekwencji DNA lub białka jakiś interesujący nas motyw, czyli inaczej
mówiąc jakiś określony fragment sekwencji. Perl oczywiście daje nam narzędzia do zrealizowania tych celów.
Zaczniemy jednak od…

Wyrażenia warunkowe

Jeżeli ktoś miał styczność z językiem BASIC z pewnością nie jest mu obca struktura IF-THEN. Wyrażenia warunkowe
pozwalają na dokonywanie wyborów zależnie od wyników przeprowadzonego testu. W Perlu występuje kilka rodzajów
wyrażeń warunkowych. Są to if, if-else i unless. W tym momencie warto zwrócić uwagę na jeden szczegół. W
konstrukcjach warunkowych Perla do przeprowadzenia testów prawda-fałsz, czyli mówiąc krótko do sprawdzenia, czy
spełniony jest interesujący nas warunek, używa się operatora „==”. Użycie pojedynczego znaku „=” byłoby błędem,
ponieważ „=” jest zarezerwowane do operacji przypisywania wartości zmiennym.

Wyrażenie if:

if(1 == 1) {

print „1 równe jest 1\n\n”;

}

daje w wyniku wydruk na ekranie „1 równe jest 1”, ponieważ w rzeczy samej jedynka równa jest jedynce.
Wyrażenie:

if(0 == 1) {

print „0 równe jest 1\n\n”;

}

nie da nam żadnego wyniku, ponieważ warunek nie jest spełniony. Zero nie równa się jedynce.
Wyrażenie tego samego typu można zapisać w inny sposób: print „1 równe jest 1” if (1 == 1); Wynik
będzie taki sam jak w pierwszym przypadku.

Wyrażenie if-else pozwala na wykonanie dwóch różnych działań w zależności od wyniku testu.

if(1 == 0) {

print „1 równe jest 0\n\n”;

} else {

print „1 nie jest równe 0\n\n”;

}

Po uruchomieniu skryptu otrzymamy wydruk „1 nie równa się 0”, zgodnie z prawdą.
Wyrażenie unless oznacza dokładnie to samo, co w języku angielskim, a mianowicie „if not”.

unless(1 == 0) {

print „1 nie równa się 0\n\n”;

}

W wyniku otrzymujemy wydruk „1 nie równa się 0”, ponieważ w rzeczy samej test warunkowy zwrócił nam prawdę, 1
nie jest równe 0.
Warto zwrócić uwagę na symbole { }. Pomiędzy nimi znajduje się część kodu, która jest wykonywana w przypadku, gdy
test warunkowy zwrócił nam w wyniku prawdę.

background image

Motywy i pętle

Strona 2

Sprawdzenie równości dwóch liczb nie jest jedynym warunkiem, który możemy podać w teście warunkowym. Inne
opcje to sprawdzenie, czy dane elementu nie są sobie równe z użyciem symbolu „!=”, czy jeden jest mniejszy od
drugiego „<”, albo większy „>”. W celu porównania łańcuchów tekstowych lub zmiennych tekstowych stosuje się
operatory eq (odpowiednik ==) oraz ne (odpowiednik !=).

Przykład 1. if-elsif-else

#!/usr/bin/perl –w
# if-elsif-else

$word = ‘MNIDDKL’;

if ($word eq ‘QSTVSGE’) {

print „QSTVSGE\n”;

}
elsif ($word eq ‘MRQQDMISHDEL’) {

print „MRQQDMISHDEL\n”;

}
else {

print „Czy \”$word\” to w ogóle peptyd?\n”;

}
exit;

Zwróćmy uwagę na symbol \” w bloku else. Postawienie \ przed znakiem cudzysłowia informuje komputer, że należy
wydrukować ten znak na ekranie, a nie jest to zakończenie tekstu drukowanego przez polecenie print.

Pętle

Pętle pozwalają na powtarzające się wykonywanie bloku poleceń ograniczonego symbolami { }. W Perlu występuje
kilka rodzajów pętli: while, for, foreach i inne. Przedstawiony poniżej przykład ilustruje zastosowanie pętli
while.

Przykład 4. Odczytywanie sekwencji z pliku, podejście 4.

#!/urs/bin/perl –w
# Odczytywanie sekwencji z pliku, podejście 4

$proteinfilename = ‘bialko.pep’;

# Najpierw otwieramy plik do odczytu, a jeżeli to się nie powiedzie komputer
# drukuje komunikat o błędzie i kończy program
unless(open(PROTEINFILE, $proteinfilename)) {

print „Nie mogę otworzyć pliku $proteinfilename!\n”;

exit;

}

# Odczytujemy sekwencję z pliku korzystając z pętli while
# drukujemy każdą odczytaną linie
while($protein = <PROTEINFILE>) {

print „### Oto następna linia z pliku: \n”;

print $protein;

}

background image

Motywy i pętle

Strona 3

close PROTEINFILE;
exit;

W pętli while odczytywana jest kolejna linia z pliku i przypisywana jest ona zmiennej $protein. Na początku pętli
sprawdzane jest, czy można odczytać linie i jeżeli jest to możliwe, to znaczy test warunkowy zwróci w wyniku prawdę,
to wykonywane są instrukcje znajdujące się pomiędzy symbolami { } (ciało pętli). Warto również zwrócić uwagę na
wykorzystanie instrukcji warunkowej unless. W przypadku, gdy nie można otworzyć pliku drukowany jest komunikat
o błędzie, a program kończy się.

Układ kodu

Wraz z rozpoczęciem używania pętli i instrukcji warunkowych warto zastanowić się nad formatowaniem kodu pisanego
przez nas programu. W gruncie rzeczy komputerowi nie zależy na tym, żeby ów kod był przejrzysty i zrozumiały. Ważne,
żeby był prawidłowy. Tymczasem dla nas przejrzystość jest ważna. Dopóki nasze programy zawierają 10 – 15 linii
problemu większego nie ma. Gorzej zaczyna się robić, kiedy ilość linii kodu przyrasta, a pętle i instrukcje warunkowe
zaczynają się zagnieżdżać…

Oto przykłady formatowania kodu z instrukcją if w pętli while. Wszystkie są poprawne i działają.

Format A

while ($zywy) {

if ($głodny) {

print „Wymagany jest pokarm\n”;

}

}

Format B

while ($zywy)
{

if ($glodny)

{

print „Wymagany jest pokarm\n”;

}

}

Format C

while ($zywy)
{
if ($glodny)
{
print „Wymagany jest pokarm\n”;
}
}

Format D

while($zywy) {if($glodny) {print „Wymagany jest pokarm\n”;}}

Formatowanie kodu jest oczywiście kwestią osobistych preferencji, jednak sposoby A i B są polecane bardziej niż C, a
tym bardziej niż D.

background image

Motywy i pętle

Strona 4

Znajdowanie motywów

Jedną z częściej wykonywanych operacji w bioinformactyce jest znajdywanie motywów, krótkich segmentów DNA lub
białek, które nas interesują. Perl posiada zestaw poręcznych narzędzi do odnajdywania motywów w łańcuchach
tekstów, co zaraz zilustrujemy przykładami.

Przykład 3. Poszukiwanie motywów

#!/usr/bin/perl –w
# Poszukiwanie motywów

# Program zapyta użytkownika o nazwę pliku zawierającego sekwencję białka
print „Proszę podać nazwę pliku zawierającego sekwencję białka: „;
$proteinfilename = <STDIN>;
unless(open(PROTEINFILE, $proteinfilename)) {

print „Nie mogę otworzyć pliku \”$proteinfilename\”\n\n”;

exit;

}

# Wczytujemy sekwencję do tablicy
@protein = <PROTEINFILE>;
close PROTEINFILE;

# Zamieniamy tablicę na jedną zmienną tekstową
# Łatwiej jest szukać motywów w jednym łańcuchu, a nie w tablicy łańcuchów
$protein = join(‘’, @protein);

# Usuwamy spacje
$protein =~ s/\s//g;

# Korzystamy z pętli i poszukujemy motywów, które będą wprowadzone z klawiatury.
# Gdy użytkownik nie wprowadzi żadnego motywu program się kończy.
do {

print „Wprowadź motyw, którego chcesz szukać: „;

$motif = <STDIN>;

# Usuwamy znak nowej linii z motywu

chomp $motif;

# Szukamy motywu

if ($protein =~ /$motif/) {

print „Znalazłem!\n\n”;

} else {

print „Nie znalazłem\n\n”;

}


# Program zakończy się, gdy użytkownik nie poda motywu
} until ($motif =~ /^\s*$/);
exit;

W programie przedstawionym powyżej znalazło się kilka nowych elementów.

background image

Motywy i pętle

Strona 5

Pobieranie danych od użytkownika: $proteinfilename = <STDIN>. STDIN jest skrótem od standard input i jest
to specjalny rodzaj uchwytu pliku, który jest związany z klawiaturą i umożliwia odczytywanie danych, które zostały z
niej wprowadzone.

Zamiana tablicy na skalar używając join. W linii $protein = join(‘’, $@protein) połączyliśmy ze sobą
wszystkie elementy tablicy @protein i zapisaliśmy je w zmiennej $protein. Poszczególne elementy składowe
nowego łańcucha rozdzielone są znakiem, który umieściliśmy pomiędzy dwa apostrofami (dwa cudzysłowie też by
działały). W naszym przypadku nie było tam nic, więc w łańcuchy zostały połączone bez żadnego specjalnego znaku
lub tekstu rozdzielającego.

Pętla do-until. Pętla ta wykonuje kod zawarty pomiędzy znakami { } dopóki spełniony jest warunek postawiony w
linii until.

Wyrażenia regularne. Pozwalają one na łatwe manipulowanie łańcuchami wszelkich rodzajów. Wyrażenie regularne
jest tak jakby uniwersalną postacią poszukiwanego przez nas motywu, umożliwiającą dopasowanie jednego lub
większej ilości łańcuchów przy wykorzystaniu do ich zapisu specjalnych symboli. Mogą być one bardzo proste, jak całe
słowo, np. /bioinformatyka/ lub bardziej skomplikowane.

W linii $protein =~ s/\s//g; wykorzystaliśmy właśnie wyrażenie regularne. Znany już operator wiązania „=~”
pozwala na przeprowadzenie operacji podstawienia w łańcuchu przechowywanym w zmiennej $protein. Po prawej
stronie operatora pojawiło się wyrażenie regularne „\s”. Symbol ten jest jednym z wielu tak zwanych meta znaków i
oznacza każdy pusty znak, to znaczy spację, znak tabulacji, znak nowej linii itd. W naszym przypadku puste znaki
zastępujemy brakiem znaku, to znaczy po prostu je usuwamy.

Linia if ($motif =~ /^\s*$/) { zawiera kolejne wyrażenie regularne. Tym razem dzięki operatorowi wiązania
możemy sprawdzić, czy wyrażenie to jest obecne w łańcuchu zapisanym w zmiennej $motif. Samo wyrażenie
regularne to: /^\s*$/. Przetłumaczyć je można jako: „dopasuj łańcuch, który od początku (oznaczonego przez ^)
zawiera zero lub więcej (oznaczone *) pustych znaków (oznaczone przez \s) aż do końca (oznaczone przez $)”.

Ostatnie wyrażenie regularne znajduje się w linii if ($protein =~ /$motif/) {. Jak widać komputer znowu
poszukuje motywów w łańcuchu przechowywanym w zmiennej $protein. Tym razem wyrażeniem regularnym jest
tekst, który wprowadził użytkownik i zachowany on został w zmiennej $motif.

Liczenie nukleotydów

Teraz spróbujemy napisać program, przy pomocy którego będziemy mogli policzyć ilość poszczególnych nukleotydów
w sekwencji DNA. Taka informacja może być przydatna na przykład do obliczania procentu par GC w danym genie. Oto
pseudokod naszego programu.

wczytaj sekwencję DNA z pliku
połącz linie w jeden ciąg $DNA

# utwórz tablicę ze zmiennej $DNA
@DNA = explode $DNA

# zainicjuj liczniki
count_of_A = 0
count_of_C = 0
count_of_G = 0
count_of_T = 0

dla każdej zasady w @DNA

background image

Motywy i pętle

Strona 6

jeżeli zasada to A

count_of_A = count_of_A +1

jeżeli zasada to A

count_of_C = count_of_C +1

jeżeli zasada to A

count_of_G = count_of_G +1

jeżeli zasada to A

count_of_T = count_of_T +1

zrobione

wydrukuj count_of_A, count_of_C, count_of_G, count_of_T

Pseudokod pozwala na ułożenie i przeanalizowanie algorytmu, który planujemy zastosować do rozwiązywania naszego
problemu, bez martwienia się o składnię wpisywanych poleceń. Krótko mówiąc jest to plan naszego programu.

Przykład 4. Określanie częstości występowania nukleotydów

#!/usr/bin/perl –w
# Określanie częstości występowania nukleotydów

print „Podaj nazwę pliku zawierającego sekwencję DNA: „;
$dna_filename = <STDIN>;
chomp $dna_filename;

unless(open(DNAFILE, $dna_filename)) {

print „Nie mogę otworzyć plku \”$dna_filename\”\n\n”;

exit;

}

@DNA = <DNAFILE>;
close DNAFILE;

$DNA = join(‘’,@DNA);
$DNA =~ s/\s//g;

# Teraz zamieniamy sekwencję zachowaną w zmiennej $DNA na tablicę
# tak, że każdy element tablicy zawiera jedną zasadę
@DNA = split(‘’,$DNA);

# inicjujemy liczniki
$count_of_A = 0;
$count_of_C = 0;
$count_of_G = 0;
$count_of_T = 0;
$errors = 0;

# Używając pętli liczymy ilość poszczególnych zasad
foreach $base (@DNA) {

if ($base eq ‘A’) {

++$count_of_A;

}

elsif ($base eq ‘C’) {

background image

Motywy i pętle

Strona 7

++$count_of_C;

}

elsif ($base eq ‘G’) {

++$count_of_G;

}

elsif ($base eq ‘T’) {

++$count_of_T;

} else {

print „Błąd, nie znam zasady: $base\n”;

++$errors;

}

}

# Drukujemy wyniki
print „A = $count_of_A\n”;
print „C = $count_of_C\n”;
print „G = $count_of_G\n”;
print „T = $count_of_T\n”;
print „Błędy = $errors\n”;

exit;

Zwróćmy uwagę na nowe szczegóły. Linia @DNA = split(‘’,$DNA); zamienia zmienną $DNA na tablicę @DNA.
Funkcja split jest przeciwieństwem join i również w niej występują apostrofy. Pomiędzy apostrofami możemy
umieścić znak rozdzielający, po napotkaniu którego funkcja będzie tworzyć kolejne elementy. Wstawienie ‘’, czyli
brak znaku rozdzielającego oznacza, że elementy tablicy będą zawierać pojedyncze znaki. W powyższym przykładzie
pojawiła się również inicjalizacja zmiennych, to znaczy tworzenie zmiennej i przypisywanie jej wartości, np.
$count_of_A = 0.
W przykładzie pojawiła się również nowa pętla , foreach. Jest to bardzo wygodna konstrukcja, pozwalająca na
powtarzanie bloku kodu dla wszystkich elementów występujących w tablicy, bez potrzeby znania ich ilości.

W przykładzie 4. znalazło się również wyrażenie typu ++$count. Jest to wygodny sposób zwiększania wartości
zmiennej $count i odpowiada ono wyrażeniu $count = $count + 1, lub w wersji skróconej $count += 1.

Słów kilka o liczbach. Perl w dosyć sprytny sposób podchodzi do zmiennych skalarnych. Przykładowo zmienną można
zapisać jako liczbę l1234 lub jako łańcuch ‘1234’. Perl traktuje obie te zmienne jako łańcuchy podczas drukowania i
jako liczby podczas wykonywania działań arytmetycznych. Zilustruje to następny przykład.

Przykład 5. Liczby i ciągi w Perlu

#!/usr/bin/perl –w
# Liczby i ciągi w Perlu

$liczba = 1234;
$ciag = ‘1234’;

# drukujemy zmienne
print $liczba, „ „, $ciag, „\n”;

# dodajemy zmienne jako liczby
$liczba_lub_ciag = $liczba + $ciag;

background image

Motywy i pętle

Strona 8

print $liczba_lub_ciag, „\n”;

# połącz zmienne jako ciągi
$liczba_lub_ciag = $liczba . $ciag;
print $liczba_lub_ciag, „\n”;

exit;

Operacje na łańcuchach

Aby dostać się do poszczególnych znaków łańcucha nie trzeba go od razu zamieniać na tablicę w sposób pokazany w
przykładzie 4. Co więcej, takiego rozwiązania należy unikać, ponieważ zabiera ono dużo pamięci. Jeżeli tekst jest krótki
to nie ma problemu, jednak jeżeli chcielibyśmy zrobić tablicę zawierającą na przykład genom komara to mogłoby być
trochę gorzej.

Przykład 6. Określanie częstości występowania nukleotydów, podejście 2

#!/usr/bin/perl –w
# Określanie częstości występowania nukleotydów

print „Podaj nazwę pliku zawierającego sekwencję DNA: „;
$dna_file = <STDIN>;
chomp $dna_file;

# Czy plik istnieje?
unless (-e $dna_file) {

print „Plik \”$dna_file\” nie istnieje.\n”;
exit;

}

# Czy można otworzyć plik?
unless (open(DNAFILE, $dna_file)) {

print „Nie mogę otworzyć pliku \”$dna_file\”.\n”;

exit;

}

@DNA = <DNAFILE>;
close DNAFILE;

$DNA = join(‘’, @DNA);
$DNA =~ s/\s//g;

$count_of_A = 0;
$count_of_C = 0;
$count_of_G = 0;
$count_of_T= 0;
$errors = 0;

# Tworzymy pętlę, która sprawdza kolejne nukleotydy w ciągu DNA
for ($position = 0; $position < length $DNA; ++$position) {

$base = substr($DNA, $position, 1);
if ($base eq ‘A’) {

background image

Motywy i pętle

Strona 9

++$count_of_A;

} elsif ($base eq ‘C’) {

++$count_of_C;

} elsif ($base eq ‘G’) {

++$count_of_G;

} elsif ($base eq ‘T’) {

++$count_of_T;

} else {

print „Błąd, nie znam zasady: $base\n”;

}

}

# Drukujemy wyniki
print „A = $count_of_A\n”;
print „C = $count_of_C\n”;
print „G = $count_of_G\n”;
print „T = $count_of_T\n”;
print „Błędy = $errors\n”;
exit;

W kodzie pojawiła się linia unless (-e $dna_filename) {, która sprawdza, czy plik o podanej nazwie istnieje
(operator „-e”). Zauważyć także można kolejną pętlę, for. Blok kodu
for ($position = 0; $position < length $DNA, ++$position) {

# tutaj zrób coś

}

odpowiada pętli while:

while ($position < length $DNA) {

# tutaj zrób coś

++$position;

}

Funkcja length zwraca długość łańcucha, który podaliśmy jej jako argument.
Funkcja substr w linii $base = substr ($DNA, $position, 1); wybiera z ciągu przechowywanego w
zmiennej $DNA fragment o długości 1 znaku rozpoczynając od pozycji $position.

Zapisywanie do plików

Jeżeli potrafimy odczytać zawartość pliku warto byłoby również móc zapisać dane do pliku. Perl oczywiście daje taką
możliwość co za chwilę zobaczymy w kolejnym przykładzie.

Przykład 7. Określenie częstości występowania nukleotydów, podejście 3

#!/usr/bin/perl –w
# Określanie częstości występowania nukleotydów

print „Podaj nazwę pliku zawierającego sekwencję DNA: „;
$dna_filename = <STDIN>;
chomp $dna_filename;

# Czy plik istnieje?
unless (-e $dna_filename) {

background image

Motywy i pętle

Strona 10

print „Plik \”$dna_filename\” nie istnieje.\n”;
exit;

}

# Czy można otworzyć plik?
unless (open(DNAFILE, $dna_filename)) {

print „Nie mogę otworzyć pliku \”$dna_filename\”.\n”;

exit;

}

@DNA = <DNAFILE>;
close DNAFILE;

$DNA = join(‘’, @DNA);
$DNA =~ s/\s//g;

# Inicjujemy liczniki
$a = 0; $c = 0; $g = 0; $t = 0; $e = 0;

# Używamy wyrażenia regularnego i pięciu pętli while
# do obliczania ilości odpowiednich zasad i błędów
while($DNA =~ /a/ig) {$a++};
while($DNA =~ /c/ig) {$c++};
while($DNA =~ /g/ig) {$g++};
while($DNA =~ /t/ig) {$t++};
while($DNA =~ /[^acgt]/ig) {$e++};

print „A=$a, C=$c, G=$g, T=$t, błędy=$e\n”;

# Zapisujemy wynik w pliku „ilość_zasad”
$outputfile = „ilość_zasad.txt”;

unless(open(COUNTBASE, „>$outputfile”)) {

print „Nie mogę otworzyć pliku \”$outputfile\” do zapisu\n\n”;

exit;

}

print COUNTBASE „A=$a, C=$c, G=$g, T=$t, błędy=$e\n”;

close COUNTBASE;

exit;

Pętla while($dna =~ /a/ig) {$a++} zawiera test warunkowy w którym występuje wyrażenie regularne /a/ z
modyfikatorami ig. Modyfikator „i” powoduje, że dopasowywane są zarówno litery małe jak i wielkie, modyfikator
„g” powoduje przeszukiwanie całego łańcucha. Blok {$a++} jest wykonywany w przypadku gdy test warunkowy
zwrócił prawdę i po prostu podnosi o jeden wartość zmiennej $a.

Polecenie open(COUNTBASE, „>$outputfile”) otwiera do zapisu plik, którego nazwa przechowywana jest
w podanej zmiennej. Sam zapis odbywa się za pomocą polecenia print, z tym, że w tym przypadku precyzujemy, że
drukowanie ma się odbywać do uchwytu pliku, czyli krótko mówiąc dane mają być zapisane w pliku.

background image

Motywy i pętle

Strona 11

Obliczenia ilości zasad poszczególnych rodzajów można dokonać również za pomocą operatora transliteracji tr.
Dokonuje on nie tylko podstawienia odpowiednich liter, ale również zwraca ilość wykonywanych podstawień. To
rozwiązanie jest najszybsze z czterech zaprezentowanych. W tym przypadku litery po prostu usuwane, bo
zastępujemy je brakiem znaku.

$a = ($dna =~ tr/Aa//);
$c = ($dna =~ tr/Cc//);
$g = ($dna =~ tr/Gg//);
$t = ($dna =~ tr/Tt//);

Funkcja nie przyjmuje znaków, które nie są literami. Dlatego też ciężko byłoby policzyć znaki, które literami nie są. To
też da się obejść. Najpierw należy usunąć wszystkie zasady, licząc przy okazji ilość usuniętych znaków, a następnie
sprawdzić jaką długość ma pozostały łańcuch.

$zasady = ($dna =~ tr/ACGTacgt//);
$nie_zasady = ((length $dna) - $zasady);

Zadania

1. Napisać program zawierający nieskończoną pętlę. Test warunkowy musi zawsze zwracać prawdę. Aby przerwać

wykonywanie programu można zamknąć okno wiersza poleceń, albo nacisnąć Ctrl + C.

2. Poprosić użytkownika o wprowadzenie dwóch krótkich łańcuchów DNA. Połączyć je ze sobą używając operatora

„.=”. Wydrukować połączone łańcuchy, a następnie wydrukować drugi łańcuch tak, aby otrzymać przedstawioną
poniżej sytuację.
Łańcuchy: AAAA oraz TTTT

AAAATTTT
TTTT

3. Napisać program, który wydrukuje liczby od 1 do 100. Oczywiście proszę użyć mniej niż 100 linii kodu.
4. Napisać program, który wydrukuje sekwencję łańcucha komplementarnego (w orientacji 5’-3’). Proszę nie

korzystać z funkcji s/// lub tr///. Należy użyć funkcji substr i zmieniać zasady pojedynczo.

5. Napisać program do obliczenia procentu hydrofobowych aminokwasów w sekwencji białkowej. Podręcznik do

biochemii może się przydac…

6. Napisać program, który sprawdza, czy dwie sekwencje podane jako argumenty są do siebie komplementarne.
7. Napisać program, który poda procent par GC w danej sekwencji.
8. Napisać program, który zamienia dwie zasady w określonej pozycji w sekwencji DNA. Można spróbować

wykorzystać funkcję substr lub splice.

9. Napisać program, który zapisuje czasowy plik, a następnie usuwa go. Funkcja unlink usuwa plik, na przykład:


unlink „tmpfile”

Program powinien sprawdzić, czy usunięcie pliku powiodło się.


Wyszukiwarka

Podobne podstrony:

więcej podobnych podstron