Witam! Na początek chciałbym się przywitać z użytkownikami forum, ponieważ jest to mój pierwszy wpis. W programowaniu również jestem stosunkowo świeży, ale to nie czas na opowiadanie mało interesujących życiowych historii ;)
Jakiś czas temu trafiłem na portal codewars.com, na którym można w bardzo fajny sposób przetestować zdobytą wiedzę rozwiązując rozmaite zamieszczane tam problemy. I właśnie w tej sprawie chciałbym się do Was zwrócić. Bawię się tam już od jakiegoś czasu i kilka zadań udało mi się rozwiązać, jednak idąc za radą ludzi mądrzejszych ode mnie, którzy nie jednokrotnie powtarzali, aby wyjść ze swoim kodem do ludzi i poddać go pod krytykę, zdecydowałem, że nadszedł czas aby się zebrać i pokazać moje chaotyczne linijki :D
Zajmowałem się ostatnio poniższym zadaniem:
https://www.codewars.com/kata/591eab1d192fe0435e000014
Dokładna treść jest podana w powyższym linku, natomiast w skrócie chodzi o to, że jesteśmy, na jakimś piętrze parkingu(jest to określone) i musimy zwrócić najkrótszą drogę na zewnątrz(czyli na parter i do wyjścia). Nie będę tutaj za dużo pisał, ponieważ dokładnie opisano to w linku, który podałem, a ja nie chcę tego powielać.
Przechodząc do sedna, poniżej zamieszczam moje rozwiązanie tego zadania:
namespace CodeWars
{
public class Kata
{
public string direction(int position, int exit)
{
if (position - exit == 0) return "";
return (position-exit>0) ? "L" + (position-exit) + " " : "R" + System.Math.Abs(position - exit) + " ";
}
public int[] foundTwo(int[,] carpark)
{
for(int i=0;i<carpark.GetLength(0);++i)
{
for (int j = 0; j < carpark.GetLength(1); ++j)
{
if (carpark[i, j] == 2) return new int[2] { i, j };
}
}
return new int[0];
}
public string[] escape(int[,] carpark)
{
int[] foundPosition = foundTwo(carpark);
int position = foundPosition[1];
int exit = -1;
string answer = "";
string[] answer_;
int counter = 1;
bool firstIteration = true;
for (int i = foundPosition[0]; i <carpark.GetLength(0); ++i)
{
if (!firstIteration)
{
if (carpark[i, position] == 1)
{
counter++;
continue;
}
answer += "D" + counter + " ";
counter = 1;
}
firstIteration = false;
for (int j = 0; j < carpark.GetLength(1); ++j)
{
if (carpark[i, j] == 1)
{
exit = j;
break;
}
}
if (exit == -1)
exit = carpark.GetLength(1) - 1;
answer += this.direction(position, exit);
position = exit;
exit = -1;
}
if (answer.Length > 0) answer_ = answer.Trim().Split(' ');
else answer_ = new string[0];
return answer_;
}
}
}
Chciałbym, aby ktoś kto byłby na tyle miły i chciałby poświęcić trochę czasu ocenił to rozwiązanie. Dopiero zaczynam taką poważniejszą zabawę w C#, przez co każde moje rozwiązanie uważam za przesadnie złożone. Może to złudne wrażenie, a może wcale nie, właśnie tego chciałbym się dowiedzieć, tzn. jak zdaniem innych osób prezentuje się powyższy kod.
Zanim jednak oddam go w Wasze ręce jeszcze kilka słów wyjaśnienia mojego procesu myślowego podczas jego budowy. Najpierw sposób budowania tablicy, która powinna zostać zwrócona. Na samym początku nie znamy jej rozmiaru i wydaje mi się, że nie sposób go jednoznacznie wyznaczyć już na starcie. W tym miejscu miałem w zasadzie dwa pomysły jak sobie z tym poradzić, pierwszy to wykorzystać typy generyczne używając klasy ArrayList, jednak nie czuje się jeszcze w tym temacie zbyt swobodnie. Nie chciałem długo ślęczeć nad dokumentacją, czego wymagałby poprzedni pomysł, dlatego też wymyśliłem to inaczej, tzn. utworzyć odpowiedź jako ciąg znaków(czyli klasa String), gdzie każdy element tablicy jest rozdzielony spacją, a następnie pocięcie tego przy użyciu metody Split i zapakowanie do tablicy, którą trzeba zwrócić. Wydaje mi się, że sposób taki jest mało profesjonalny.
Co do samego zadania, najważniejsze funkcje postanowiłem wyciąć, z głównego kodu programu i spakować w formie metod(te na samej górze, myślę, że dzięki temu to wszystko jest czytelniejsze i bardziej przejrzyste). W początkowej fazie programowania wyszukiwanie liczby 2 wykonywało się za każdym obrotem pętli. Pętla sprawdzała każdą liczbę i przyrównywała ją do 2 oraz do 1 i odpowiednio zapisywała. Stwierdziłem jednak, że dwójka może wystąpić tylko raz dlatego taki sposób jest mało optymalny, bo program robi coś co jest bezużyteczne. Kod z metody direction nie wymagał natomiast wycięcia, ale również wydaje mi się, że poprawiło to czytelność.
Samo działanie kodu jest następujące:
1. Znajdujemy liczbę 2 oraz zapisujemy numer piętra i (indeks) pozycję tej liczby w tablicy
2. Pozycję przypisujemy zmiennej Position
3. Do zmiennej exit przypisujemy wartość -1(w tej zmiennej będzie przechowywany indeks miejsca, gdzie są schody)
4. Łańcuch answer będzie przechowywał odpowiedź przed pocięciem a tablica answer_ po pocięciu
5. Zmienna counter określa o ile pięter będziemy się opuszczać w dół, natomiast fistIteriaton określa, czy pętla dopiero się zaczyna czy też nie.
6. Wskakujemy w pętlę, iteracji ma być tyle ile pięter mamy do sprawdzenia(to na którym stoimy, oraz wszystkie pod nami)
Szukałem sposobu na rozwiązanie tego bez tej zmiennej(bool), jednak nie potrafię znaleźć metody. Chodzi głównie o to, że nie zawsze będziemy zaczynać od najwyższego piętra(więc np. warunek i>0 odpada) a na samym początku pętli znajduje się kod odpowiedzialny za zejście piętro niżej, dlatego przy pierwszym obrocie chcemy go ominąć.
7. Po przeskoczeniu kodu opuszczania piętra, w każdym kolejnym obrocie już chcemy go uwzględniać, dlatego zmienna bool jest odwracana.
8. Następna pętla szuka miejsca, gdzie są schody(czyli liczby 1), gdy to zrobi natychmiast przerywa pętlę. Jedynka jest tylko jedna, zatem nie ma potrzeby dalszego działania.
9.Jeśli liczba 1 nie została odnaleziona to zmienna exit ma wartość początkową a to oznacza, że na piętrze są same 0(czyli jesteśmy na parterze).
10.Jeśli jesteśmy na parterze to schody są najbardziej po prawo
11. Metodą direction z uzyskanymi danymi wyznaczam odpowiedni symbol, opisany w poleceniu(odejmuje miejsce, gdzie zacząłem od schodów i na tej podstawie ustalam w którą stronę i o ile się poruszam. Jeśli wynik jest równy 0 oznacza to, że nie ruszyłem się)
12. W tym momencie stoję tam, gdzie schody, więc position=exit natomiast exit wraca do wartości domyślnej
13. Wracamy na górę pętli i tym razem schodzę o piętro niżej, jeśli w miejscu, gdzie właśnie zeskoczyłem jest 1 to zwiększam liczbę pięter, które przeskakuję o 1 oraz pomijam iterację pętli(ponieważ nie muszę szukać schodów). Jeśli nie stoję na 1, to wyznaczam symbol i resetuje licznik.
I w taki sposób się to kręci, aż sprawdzę wszystkie piętra i potnę wynik. Ostatni if sprawdza, czy łańcuch jest dłuższy niż 0, w poleceniu powiedziano, że nie zacznę na schodach ale mogę zacząć na parterze, w miejscu, gdzie jest wyjście. Wtedy tablica będzie pusta, bo nie wyznaczę żadnego symbolu. Ostatecznie zwracam tablicę z odpowiedziami.
Wybaczcie, że nieco się rozpisałem, ale chciałem mniej więcej dobrze opisać co i jaką funkcję miało spełniać w rozwiązaniu. Jeśli ktoś byłby na tyle miły aby przez to przebrnąć i ocenić sposób rozwiązania tego zadania(a przede wszystkim sam kod) to byłbym wdzięczny, wszelkie wskazówki będą dla mnie bardzo cenne ponieważ bardzo zależy mi na jak najrzetelniejszej nauce programowania.
Z góry dziękuję za odpowiedzi i wytrwałość :)