Programowanie - blog

Java, Python, programowanie, ciekawostki programistyczne, algorytmy, Spring, Hibernate

wtorek, 20 listopada 2012

jQuery - animacje wykonywane bezpośrednio po sobie

Dziś sam siebie postawiłem przed nietrywialnym jak dla mnie problemem (po tym krótkim wstępie jasne już jest, iż wyjadacz w kwestii jQuery ze mnie żaden). Chodziło otóż o to, aby wykonać następujące po sobie animacje elementów z użyciem jQuery.
$('#div1').slideUp('slow');
$('#div2').slideDown('slow');
Powyższy kod niestety efektu takiego nie zapewni, obie animacje wykonuja się jednocześnie. Na szczęście, jak wynika z dokumentacji slideUp/Down, podobnie jak większość funkcji jQuery pozwala jako parametr przekazać metodę, wywoływana, w tym konkretnym przypadku, po zakończeniu animacji. Przykładowy kod poniżej:
$('#div1').slideUp('slow', function() {
   $('#div2').slideDown('slow');
});

sobota, 20 lutego 2010

Mój pierwszy SRM ;)

SRM - single round match, a więc runda stałych zawodów TopCoder. TopCoder jest amerykańską firmą zajmującą się organizacja zawodów programistycznych. W zawodach może wziąć udział praktycznie każdy, wystarczy zarejestrować się na www.topcoder.com i wybrać odpowiednią dla siebie "dyscyplinę". Mnogość konkurencji w jakich można rywalizować, na początku może przyprawić o zawrót głowy, ja jednak trafiłem tam w jedynym konkretnym celu, sprawdzeniu się w konkurencji polegającej na implementacji algorytmów realizujących postawione zadanie. Dlaczego akurat taki wybór? Głównie ze względu na bardzo zróżnicowany poziom zadań. Nie ukrywam, że choć bardzo się staram, moje umiejętności programistyczne nadal są niewielkie, a w konkurencji "Algorithm", każdy znajdzie coś dla siebie. Po odpaleniu appletu Competition Area w dziale Practice ma się do dyspozycji archiwum zadań ze wszystkich meczów, które zostały do tej pory rozegrane. Podczas implementacji rozwiązania z poziomu appletu można sprawdzić działanie programu na przykładach zawartych w opisie zadania. W momencie kiedy uważamy, że algorytm działa poprawnie zgłaszamy swoje rozwiązanie. Liczba przyznanych punktów zależy od tego jak szybko udało nam się wysłać rozwiązanie. Na koniec pozostaje tylko wybrać opcję "System test" aby sprawdzić nasze rozwiązanie na przygotowanym przez organizatorów zestawie testowym. Przed moim pierwszym meczem rozwiązałem tylko około 20 zadań i to zresztą tych najłatwiejszych. Zawody zaczęły się o godzinie 3:00 naszego czasu. Niestety nie mogę ich zaliczyć do udanych, gdyż udało mi się rozwiązać tylko jedno, najłatwiejsze zadanie (najsłabiej punktowane) i to w niezbyt dobrym czasie. Na pewno duży wpływ na wynik miał lekki stres i pora rozgrywki, niemniej jednak jestem bardzo zadowolony z samego faktu, że wziąłem w nich udział. Uważam, że jest to świetny sposób rozwoju swoich umiejętności. Podczas tej rundy popełniłem wiele błędów, jednak równie dużo się nauczyłem. Zdobyłem punkty rankingowe, których ilość plasuje mnie, mniej więcej , w połowie stawki. Aktualnie nadal rozwiązuję zadania z historycznych konkursów i z niecierpliwością czekam na kolejną rundę. W tym miejscu chciałbym ostrzec każdego, kto po przeczytaniu tego tekstu zamierza sprawdzić się w takich zawodach: UWAGA, TO WCIĄGA ;).

piątek, 12 lutego 2010

Zastosowań wskaźników ciąg dalszy - tablice wielowymiarowe

W poprzednim poście opisałem sposób dynamicznego przydziału pamięci dla tablicy jednowymiarowej. Przy okazji chciałbym powiedzieć, ze do poprzedniej notki wkradł się błąd dotyczący funkcji calloc, który już poprawiłem. W tej notce również będzie o tablicach, tym razem zajmiemy się jednak tablicami wielowymiarowymi. O ile w przypadku tablicy wielowymiarowej, gdzie znamy jej rozmiar już w trakcie tworzenia programu, nie ma wielkich problemów, to jednak, w momencie kiedy zechcemy przydzielić dla niej pamięć dynamicznie, sprawa nieco się komplikuje. Mam jednak nadzieje, że po przeczytaniu tej notki, wiele się wyjaśni. Na początek zastanówmy się jakie "narzędzia" będą nam potrzebne. Poznane do tej pory "normalne" sposoby tworzenia tablicy oraz wskaźniki niestety nie wystarczą. Tym czego nam potrzeba jest: wskaźnik na wskaźnik. Hmm... zdaje sobie sprawę, że nie brzmi to zbyt zachęcająco, szczególnie, jeżeli nie masz jeszcze dostatecznej wprawy w "żonglowaniu" normalnymi wskaźnikami. Otóż wskaźnik na wskaźnik, jak sama nazwa wskazuje, nie jest normalny wskaźnikiem, którego poznaliśmy do tej pory, i który pokazywał na zmienną. Jest to konstrukcja, która pokazuje na wskaźnik. Tworzenie tablicy dwu-wymiarowej zaczniemy więc od wskaźnika, który będzie pokazywał na jednowymiarową tablicę wskaźników, które to z kolei będą pokazywać na kolejne jednowymiarowe tablice, będące wierszami naszej tablicy dwu-wymiarowej. Aby lepiej to zrozumieć polecam przeanalizować kod:

int wiersze; // tutaj trzeba wczytać ilość wierszy
int kolumny; // tutaj trzeba wczytać ilość kolumn

int **tablica; // tworzymy wskaźnik na wskaźnik

tablica = new int*[wiersze]; // tworzymy tablice wskaźników do wierszy

for(int i = 0; i < wiersze; i++)
{
tablica[i] = new int[kolumny]; // tworzymy kolumny
}

Ja łatwo się domyślić, kasowanie tablicy, również musi przebiegać w nieco odmieniony sposób. Musimy zacząć od kasowania kolumn, a dopiero potem skasować tablicę wskaźników do wierszy. Oto kod:

for(int i = 0; i < wiersze; i++)
{
delete[] tablica[i];
}
delete[] tablica;

Analogicznie postępujemy w przypadku tworzenia tablic o większej liczbie wymiarów. Kody zostały podane w wersjach z wykorzystaniem operatora new. W ramach ćwiczeń polecam samodzielnie przećwiczyć sobie deklarowanie tablic wielowymiarowych z wykorzystaniem malloc, calloc i free.

wtorek, 9 lutego 2010

Ciąg dalszy o dynamicznej alokacj pamięci

W poprzedniej notce powiedzieliśmy sobie co nieco o funkcjach malloc, calloc oraz free. Jak poprzednio wspomniałem są to funkcje znane z języka C. Nie znaczy to oczywiście, że programiści C++ nie mogą ich używać. Z pewnych względów jednak nie powinni tego robić. Twórcy języka C++ oddają w nasze ręce nowe i lepsze rozwiązanie. Używanie funkcji new i delete ma szczególne znaczenie w przypadku programowania obiektowego, ale o tym jeszcze nie teraz. Zobaczmy jak za pomocą tych funkcji osiągnąć ten sam efekt co poprzednio:

int tablica*;
int rozmiar;
scanf("%d", &rozmiar);
tablica = new int[rozmiar];
...
delete[] tablica;

Jak widać zamiast dość skomplikowanego (przynajmniej na pierwszy rzut oka) wywołania malloc w dodatku z rzutowaniem, mamy tylko jedno słowo kluczowe new. Przy usuwaniu tak utworzonej tablicy trzeba jednak pamiętać aby użyć polecenia delete[] a nie samego delete, które to spowodowało by oddanie do systemu tylko pierwszego elementu (indeksu) tej tablicy. Nieużywanemu wskaźnikowi, podobnie jak w poprzednim przypadku, należy przypisać adres NULL.

poniedziałek, 8 lutego 2010

Słówko o malloc() calloc() i free()

W poprzedniej notce opisane zostało wykorzystanie wskaźników i referencji w celu modyfikacji przez funkcję pewnych zmiennych zadeklarowanych wcześniej w programie. Jakkolwiek jest to wiedza bardzo potrzebna, praktyczna i przydatna, nie wyczerpuje jednak możliwości i dziedzin zastosowania "osławionych" wskaźników.
Podczas pisania programu bardzo często wykorzystujemy tablice. Zakładamy, że czytelnik posiada elementarna wiedzę, jak to czym są i do czego one służą. Tablice w programie możemy zadeklarować w bardzo prosty sposób, jeżeli z góry znamy jej rozmiar, np.:

int tablica[10]

W tym wypadku tworzona jest 10 - elementowa tablica zmiennych typu int. Sprawa nieco się komplikuje, w momencie kiedy zależy nam na tym aby rozmiar mógł być zadeklarowany w czasie działania aplikacji, np.: po wprowadzeniu przez użytkownika pewnej liczby. Niestety rozwiązanie przedstawione poniżej nie zadziała:

int rozmiar;
scanf("%d", &rozmiar);
int tablica[rozmiar];

Powodem jest to, iż rozmiar tablicy deklarowanej w ten sposób musi być znany w trakcie kompilacji programu. Nie wnikajmy dlaczego tak się dzieje, gdyż na chwilę obecną z praktycznego punktu widzenia nie jest to aż tak ważne. Pomyślmy lepiej nad rozwiązaniem tej sytuacji. Jako, że miało być coś o wskaźnikach, nie trudno się domyślić, iż to właśnie one są odpowiednim narzędziem, które pozwoli nam rozwiązać ten problem.
To co teraz zrobimy, w przyszłości (nie, wcale nie znaczy to "od jutra", jutro to bardzo pracowity dzień, każdy z nas chyba coś od jutra zaczyna, więc w tym wypadku proponuje zacząć już od teraz) nazywać będziemy dynamiczną alokacją pamięci. Jako, że zdecydowanie wolę objaśniać napisany kod, niż pisać o tym jak go napisać, zacznijmy od kilku linijek programu. Na początek na celownik bierzemy funkcję malloc, znaną z biblioteki języka C.

int* tablica;
int wielkosc;
scanf("%d", &wielkosc);
tablica = (int*)malloc(wielkosc * sizeof(int));
...
free(tablica);
tablica = NULL;

Co my tu mamy... zacznijmy od początku. W pierwszej linijce deklarujemy wskaźnik na zmienną typu int. Druga i trzecia linia, mam nadzieję, nie wymagają wyjaśnień. To co nas najbardziej interesuje, dzieje się w linijce czwartej. Zauważmy, że mamy już wskaźnik na zmienna typu int, ale jak do tej pory nigdzie nie utworzyliśmy zmiennej, na którą mógłby on pokazywać. Czas to zmienić i w tym momencie trzeba sobie powiedzieć kilka słów o funkcji malloc. Jak można się domyślić po tym iż wynik jej działania przypisujemy do naszego wskaźnika, zwraca ona wskaźnik. Nie jest to jednak wskaźnik żadnego konkretnego typu, a konkretnie jest to wskaźnik na void. Niezbędne zatem jest rzutowanie wyniku działania funkcji na wskaźnik interesującego nas typu. Sama funkcja malloc ma za zadanie przydzielić pewien obszar pamięci, którego wielkość określamy jako parametr podczas wywołania funkcji. Zwrócony wskaźnik pokazuje na początek tego obszaru. Jak łatwo się domyślić, w naszym wypadku funkcja przydziela obszar rozmiaru ("wielkosc" * rozmiar zmiennej int) a więc rezerwuje miejsce dla wprowadzonej przez użytkownika ilości zmiennych typu int. Wiedząc, że tablica to nic innego jak ciągły obszar pamięci wielkości wielokrotności rozmiaru pojedynczej zmiennej danego typu, dochodzimy do wniosku, że właśnie utworzyliśmy tablice o zadanym przez użytkownika rozmiarze.
Od tej pory możemy jej używać w identyczny sposób, jak poznane wcześniej tablice. Trzeba jedna pamiętać aby, kiedy tablica nie jest już nam potrzeba, zwolnić przydzieloną jej pamięć. Służy do tego funkcja free, wywołana tak jak w powyższym przykładzie.

Funkcja calloc spełnia podobne zadanie, jednak podczas jej wywołania, przydzielany obszar jest inicjowany zerami. W przypadku funkcji malloc, zawartość obszaru jest nieokreślona, więc jest w nim to, co wcześniej było w tym miejscu pamięci i bynajmniej nie są to zera ;).

Bardzo dobrą, a wręcz wymaganą jest, aby po zwolnieniu pamięci, przypisać wskaźnikowi, który na nią wcześniej wskazywał, wartość NULL. Pomoże to uchronić się przed błędem ochrony pamięci, gdybyśmy przez nieuwagę w dalszej części programu spróbowali odwołać się do tego obszaru pamięci, który przecież już do nas nie należy.

W następnej notce postaram się opisać funkcje new oraz delete.