Algorytmy numeryczne i pliki wsadowe Drukuj
Ocena użytkowników: / 1
SłabyŚwietny 
Wpisany przez Tomasz Stasiak   
niedziela, 23 października 2011 21:56

wynik skryptuZacznijmy od początku - czym są pliki wsadowe, a czym algorytmy numeryczne? Pliki wsadowe są wykorzystywane w różnych systemach operacyjnych (np. Linux, Windows, DOS, etc.) do wykonywania prostych zadań związanych głównie z zarządzaniem komputerem dzięki temu możemy np. zautomatyzować pewne zadania; w czasach DOS-a pliki wsadowe były wykorzystywane do tworzenia ustawień przy starcie systemu. Jeśli chodzi natomiast o algorytmy numeryczne, to mówiąc najprościej są to wszystkie algorytmy, które wykonują operacje na liczbach (żeby nie było wątpliwości: chodzi o to, że to ma być główny cel algorytmu, a nie np. operacja wykonywana przy przetwarzaniu tekstu :)). Ten tekst (a w zasadzie cykl tekstów) będzie dotyczył plików wsadowych w systemie Windows (acz w DOS-ie powinno działać także); mają one rozszerzenie *.cmd lub *.bat. Dlaczego ten OS? Bo pracuję aktualnie na takim, nie innym systemie.

 

Ale do rzeczy: jak nie trudno wywnioskować ze wstępu (który może nie był zbyt pasjonujący, ale potrzebny), pliki wsadowe raczej nie są tym, o czym pomyślimy, kiedy będziemy mieli jako zadanie napisanie programu/skryptu, który ma coś liczyć. Jest ku temu kilka powodów: głównie - treść plików wsadowych to w większości wywołania innych programów, brak normalnej pętli for, czy dziwnie działająca instrukcja if. Chociaż i tak największym mankamentem jest sposób traktowania zmiennych - domyślnie wszystkie zmienne (środowiskowe BTW) są traktowane jako tekst, co trochę utrudnia zadanie; nie można użyć tego typu konstrukcji:

 set var=1
 set var=%var% + 1
 echo %var%

Ponieważ da ona następujący wynik: %var% = 1 + 1 (zostało to potraktowane jako tekst, a nie jako liczba, jak można by oczekiwać). Okazuje się, że mamy tu do czynienia z typowaniem dynamicznym słabym i na dodatek, aby interpreter (np. cmd.exe lub command.com) traktował te zmienne jako liczby musimy użyć set z przełącznikiem /A. Rozpatrzmy jeszcze raz nasz przykład:

 set /A var=1
 set /A var=%var% + 1
 echo %var%

Teraz wynikiem działanie będzie %var% = 2, czyli to, co chcieliśmy :). Wracając do wspomnianej pętli for - w normalnych językach programowania pętla działa w następujący sposób: sprawdzamy warunek i dopóki nie jest on spełniony wykonujemy zawartość tejże pętli. Tutaj natomiast pętla wykonuje się  tylko dla elementów podanych jako argument. Tak więc, aby wykonać pętlę z n obrotami w tradycyjny sposób musielibyśmy za każdym razem wpisywać tam właśnie te n elementów. A to chyba odpada? :) Jak więc to zrobić? Jeśli ktoś z was programował w asemblerze, to wie, że pętlę można zorganizować na 2 sposoby:

  1. za pomocą standardowych instrukcji, rejestr ecx schodzi od początkowej wartości do 0 (wtedy jest koniec pętli)
  2. z pomocą instrukcji jmp/jz/jg/je/etc, własnego warunku sprawdzanego przez test/cmp oraz etykiet

W plikach wsadowych nie mamy co prawda niczego takiego, ale na pomoc mogą nam przyjść odpowiedniki - zamiast jmp mamy instrukcję goto, a zamiast test/cmp możemy użyć if'a. Tutaj uwaga: if w plikach wsadowych ma kilka dziwnych zachowań, na które trzeba uważać:

  • jeśli istnieje ryzyko, że zmienna będzie pusta, należy oba porównywane człony umieścić w cudzysłowie: if "%a%" == "6" echo 1 Jest to spowodowane tym, że jeśli zmienna będzie pusta zostanie to zinterpretowane jako if  == 6 echo 1 (gdyby pominąć cudzysłowy)
  • nie ma typowych operatorów porównania:
  • gtr zamiast >
  • geq zamiast >=
  • lss zamiast <
  • leq zamiast <=
  • operator porównania pozostaje jak zwykle
  • wykonanie innego pliku *.bat lub *.cmd jest tak bugogenne, że lepiej unikać takich sytuacji
  • trzeba uważać, aby nie było konfliktów nazw funkcji/zmiennych/etykiet, bo mogą być problemy (to akurat standard, ale warto wspomnieć)

To już chyba wszystko, o czym warto wspomnieć, jeśli o czymś zapomniałem - wyjdzie w praniu :). Mając te wszystkie informacje możemy się zabrać za pisanie, na rozgrzewkę niech będzie skrypt wypisujący wszystkie liczby z zakresu od 1 do n (włącznie), gdzie n jest argumentem podanym z linii poleceń. Zaczynamy, oczywiście, od znanej (mam nadzieję) wszystkim linijki:

@echo off

Która ma wyłączyć wyświetlanie aktualnie parsowanej linijki; następnie można stworzyć i ustalić wartość początkową zmiennych (jeszcze jedna uwaga: wszystkie są traktowane jako globalne i pozostają nawet po wykonaniu skryptu - do zamknięcia konsoli), wyświetlić jakieś informacje o tym, kto jest autorem, czy sprawdzić, czy użyszkodnik na pewno podał argument (bo skąd mamy wiedzieć, do ilu liczyć?):

rem iterator w pętli
set /a var = 1

echo Program wypisujacy kolejne liczby calkowite od 1 do n
echo .
echo Copyright 2011 Tomasz Stasiak
echo .

Komenda REM oznacza komentarz, została wpisana dla wyjaśnienia, co się dzieje; natomiast echo . jest umieszczone, aby wyświetlić "pustą linię" - jeśli po echo nie będzie żadnego, niebiałego znaku (czyli czegoś oprócz spacji, tabulatora, znaku nowej linii, etc.), to nie zostanie wyświetlone nic; tu dzięki temu zyskujemy odstęp między pierwszą, trzecią i pozostałymi wyświetlanymi liniami. %1 jest oznaczeniem parametru przesłanego do pliku - %0 to zawsze nazwa pliku, kolejne numery mają argumenty wpisywane w konsoli. Program się wykona przy takim wywołaniu (zakładając, że nazwa pliku to liczby_od_1_do_n.bat):

C:\>liczby_od_1_do_n.bat 10

Natomiast po wywołaniu w ten sposób:

C:\>liczby_od_1_do_n.bat

Skoczy do etykiety null_arg, która ma na celu obsłużenie wywołania bez argumentu:

rem obsługa w wypadku braku argumentu
:null_arg
    echo Nie podales argumentu!
    echo .
    echo Uzycie: %0 n
    echo Gdzie:
    echo  n oznacza ilość liczb do wypisania
goto end

end jest etykietą dodaną na końcu pliku, służy do skoku, aby można było pominąć pewne sekcje, które nie mają się wykonać, widać tu jak bardzo przydatny okazuje się fakt, że %0 zawiera nazwę pliku. No dobrze, jesteśmy już przygotowani - wiemy, że użytkownik podał argument, a potrzebne nam zmienne istnieją, należy tylko utworzyć pętlę, w której wypiszemy kolejne liczby. W jaki sposób? Otóż, jak wspominałem, istnieje możliwość utworzenia etykiety i skakania do niej, dopóki argument nie jest spełniony:

rem etykieta w pętli - stary, "dobry" sposób znany z asemblera
:loop
    rem wyświetlenie liczby
    echo %var%
    rem sprawdzenie, czy zmienna nie osiągnęła wymaganego numeru
     if "%var%" == "%1" goto end
    rem inkrementacja zmiennej o 1
    set /A var += 1
rem powrót do początku pętli
goto loop

Wyjaśnienie krok po kroku, co się dzieje:

  • tworzymy etykietę :loop, która oznacza początek naszej pętli - to do niej będziemy skakać
  • wyświetlamy zmienną %var% - to jest równocześnie nasz licznik w pętli
  • sprawdzamy czy %var% jest równe argumentowi, jeśli tak, kończymy działanie, jeśli nie - idziemy dalej (dlaczego tu, a nie przed echo lub po set? Jeśli byłoby przed echo, to nie została by wyświetlona ostatnia liczba (ta, która jest równa argumentowi), taki sam wynik uzyskalibyśmy umieszczając porównanie jako ostatnie polecenie w pętli
  • w ostatniej linii tego listingu skaczemy znowu do początku pętli (skoro tu dotarliśmy, to pętla nadal ma się wykonywać

Zbierając to wszystko "do kupy" otrzymujemy:

@echo off

rem Zmienne:
rem iterator w pętli
set /a var = 1

echo Program wypisujacy kolejne liczby calkowite od 1 do n
echo .
echo Copyright 2011 Tomasz Stasiak
echo .

rem sprawdzenie, czy wszystkie argumenty są ok
 if "%1" == "" goto null_arg rem pusty argument

rem etykieta w pętli - stary, "dobry" sposób znany z asemblera
:loop
    rem wyświetlenie liczby
    echo %var%
    rem sprawdzenie, czy zmienna nie osiągnęła wymaganego numeru
     if "%var%" == "%1" goto end
    rem inkrementacja zmiennej o 1
    set /A var += 1
rem powrót do początku pętli
goto loop

rem obsługa w wypadku braku argumentu
:null_arg
    echo Nie podales argumentu!
    echo .
    echo Uzycie: %0 n
    echo Gdzie:
    echo  n oznacza ilość liczb do wypisania
goto end

:end

Jak widać, na końcu pliku pojawia się wspomniana wcześniej etykieta end, za którą nic więcej nie ma. Plik do pobrania: liczby_od_1_do_n.bat (należy zmienić rozszerzenie na .bat). Niestety, nie ma kolorowania składni, bo Joomla nie daje takiej możliwości :(

Dodatkowo, z przykrością (no, w sumie bardziej zależy dla kogo :)) informuję, że tym wpisem kończę swoją działalność publikacyjną na youthcoders.net, po czym przenoszę się na własnego bloga. To takie info, aby nie było, że zacząłem tu pisać i nagle z niewiadomych powodów wyniosłem się gdzie indziej :)