codExplainings #2 - podmieniamy znaki na cyfry

|
Robert
| komentarze | Poradniki Inne

Kontynuujemy serię wpisów objaśniających katy z codewars. Jeśli znajdujesz się tu pierwszy raz zachęcam do zapoznania się z pierwszym wpisem, który objaśnia o co chodzi w tej serii. W dzisiejszym odcinku bierzemy na warsztat katę w języku PHP o tajemniczej nazwie Replace with Alphabet Position. Prosta sprawa, na wejściu funkcji mamy ciąg znaków, natomiast zwraca kod składający się z liczb oddzielonych spacjami. Każda liczba reprezentuje jakiś znak.  Skąd wiedzieć co to za znak i jak to zmienić? Jest na to sposób. No właśnie jaki?

Na początek małe przypomnienie

Warto utrwalać sobie wiedzę, więc zacznę od początku jak wygląda funkcja oraz jak będzie wyglądać przykładowe wywołanie kodu. Funkcja w PHP składa się ze słowa kluczowego function, nazwy funkcji która w tym przypadku zapisana jest sposobem sneaky_case, nawiasów w których mogą znajdować się dane wejściowe oraz klamer (wąsów) pomiędzy którymi znajdzie się kod funkcji.

<?php
function alphabet_position(string $s): string {
  // Your code here
}

W tym przypadku funkcja korzysta ze strict types czyli ścisłego typowania które wprowadzone zostało w PHP7. Zarówno argument $s jaki przyjmuje funkcja jak i dane przez nią zwracane są w tym przypadku typu string. Przyjrzyjmy się teraz przykładowi wywołania kodu.

class AlphabetPositionTest extends TestCase {
  public function testFixed() {
    $this->assertSame('20 8 5 19 21 14 19 5 20 19 5 20 19 1 20 20 23 5 12 22 5 15 3 12 15 3 11', alphabet_position('The sunset sets at twelve o\' clock.'));
    $this->assertSame('20 8 5 14 1 18 23 8 1 12 2 1 3 15 14 19 1 20 13 9 4 14 9 7 8 20', alphabet_position('The narwhal bacons at midnight.'));
  }
}

Obiekt obsługujący testy kodu porównuje zestaw liczb z wartością jaką zwraca nasza funkcja mająca na wejściu dany string.

Skąd mam wiedzieć co robić dalej?

Najprostszym sposobem rozwiązania powyższego problemu jest po prostu googlowanie, najlepiej skupiając się na dokumentacji która zwykle jest aktualna. Czasy pod tym względem mamy bardzo komfortowe, wystarczy trochę chęci, cierpliwości oraz kreatywności by wpisać w wyszukiwarce how to convert char to int i to wszystko. Z treści zawartych tam można się dowiedzieć, że tak naprawdę każdy znak ma swoją liczbę porządkową - nazywamy do kodowaniem. PHP jest językiem, który posiada cały zestaw narzędzi do poradzenia sobie z problemami tego typu.

Doświadczone oko dojrzy, że w konwertowaniu pomijane jest rozróżnianie wielkości znaków oraz pominięte są znaki nie będące literami. W tym celu oczyścimy sobie ciąg metodą preg_replace zamieniając uprzednio ciąg na małe litery metodą strtolower, a następnie zamienimy ciąg na tablicę znaków.

$s = strtolower($s);
$s = preg_replace('#[^a-z]#', "", $s);
$s = str_split($s);

Pierwsza funkcja po prostu zamienia duże znaki na małe, natomiast druga zamienia znaki bądź całe ciągi. Przyjmuje ona trzy parametry. Pierwszym jest wyrażenie regularne, które w tym przypadku bierze pod uwagę wszystko co nie jest z zakresu liter od a do z czyli spacje i wszelkie inne symbole. W drugim parametrze definiujemy na co ma zamienić, tutaj akurat na nic - usuwa. Na końcu wskazujemy ciąg na którym ma operować. Funkcja str_split zamienia ciąg na tablicę znaków.

Transformacja tablicy - mapowanie

Skoro mamy już przygotowane wejście, czas na operację przetworzenia. Skorzystamy z metody array_map, za pomocą której będziemy mogli operować na każdym kolejnym elemencie tablicy. Pierwszym parametrem tej funkcji jest inna funkcja która na wejściu ma każdy kolejny element, a na wyjściu zwraca przetworzony. Dzięki temu możemy szybko i sprawnie przetwarzać tablice transformując je na swoje potrzeby.

$s = array_map(
  fn($ch) => ord($ch) - 96,
  $s
);

Funkcja przetwarzająca ma jedno zadanie - podany znak przełożyć na pozycję w tablicy ASCII, a następnie odjąć 96 - tyle znaków występuje przez wystąpieniem "a" w tablicy.

Ostatnia prosta

Całość już jest gotowa, należy zwrócić wynik jako ciąg oddzielony spacjami. Pomoże nam w tym funkcja implode przyjmująca jako parametry kolejno delimiter czyli spację oraz tablicę $s. W tym miejscu możemy już zwrócić wynik, bez przypisywania.

return implode(' ', $s);

Podsumowanie

Przedstawiłem dziś sześć różnych metod, każda dzięki swojemu zastosowaniu sprawiła, że interesujący nas efekt został z łatwością osiągnięty. Warto ćwiczyć i poznawać możliwości języka, niezależnie od tego w jakiej technologii programujemy. Warto jeszcze naszą funkcję zabezpieczyć przed sytuacjami, które są do przewidzenia, np. ktoś da na wejściu pusty ciąg znaków, bez tego nasz kod zwróci "-96", a powinien "". Zadba o to metoda empty która sprawdza, czy ciąg jest pusty. Warto to zrobić tuż po oczyszczeniu ciągu w przypadku gdyby ktoś podał na wejściu same spacje. Nasz kod wówczas powinien wyglądać następująco:

function alphabet_position(string $s): string {
  $s = strtolower($s);
  $s = preg_replace('#[^a-z]#', "", $s);
  if (empty($s)) {
    return "";
  }
  $s = str_split($s);
  $s = array_map(
  fn($ch) => ord($ch) - 96,
    $s
  );
  return implode(' ', $s);
}