Bardzo proste ćwiczenie. Bierzemy dwie całkiem proste funkcje rekurencyjne z poprzedniej lekcji i zamieniamy na podejście iteracyjne. Do dzieła.
Oto funkcja sprawdzająca palindrom:
function isPalindrome(str){
if(str.length === 1) return true;
if(str.length === 2) return str[0] === str[1];
if(str[0] === str.slice(-1)) return isPalindrome(str.slice(1,-1))
return false;
}
Oto jej iteracyjny odpowiednik metodą dwóch wskaźników:
function isPalindrome(s) {
let start = 0;
let end = s.length - 1;
while(start < end) {
if (s[start] !== s[end]) {
return false;
} else {
start++;
end--;
}
}
return true;
}
Jest start i finisz (indeksy). W pętli sprawdzamy czy są równe i jeden inkrementujemy, drugi dekrementujemy.
A teraz pokażę, jak tego nie robić (w języku Python, przykład z sieci):
def isPalindrome(word):
midPoint = len(word)//2
palindrome = True
for i in range(0,midPoint):
left = word[i]
right = word[len(word)-i-1]
if left!=right:
palindrome=False
break
return palindrome
Bawimy się w jakiś mid-pointy jak w binary search, pętla for zupełnie nieczytelna, podobnie jak logika, tworzymy zmienne left i right do których przypisujemy wartości pod indeksem i następnie nazwy tych zmiennych porównujemy, nie wyrywamy się z pętli returnem tylko breakiem, choć wcale nie musimy, bo potem i tak nic dalej nie robimy.
Takich kodów nie lubię. Jest to ewidentne podejście muszę napisać palindrom w wersji iteracyjnej, choć pojęcia nie mam, jak się do tego zabrać. Byle działało. No to akurat działa, trzeba przyznać.
Zobaczmy na nasz przykład i dlaczego „kajak” działa. Zastanówmy się nad jednym – czym będą start i end w każdym obrocie pętli.
Pseudokod:
var word = "kajak"
var start = 0
var end = 5 - 1
warunek = while(start < end)
po każdej pętli = start++, end--
obrót 1:
word[start] = "k" (0)
word[end] = "k" (4)
obrót 2:
word[start] = "a" (1)
word[end] = "a" (3)
obrót 3:
word[start] = "j" (2)
word[end] = "j" (2)
obrót 4:
nie istnieje, bo start (3) nie jest mniejszy od end (1)...
Dobra, a teraz someRecursive:
function someRecursive(array, callback) {
if (array.length === 0) return false;
if (callback(array[0])) return true;
return someRecursive(array.slice(1),callback);
}
Jak to ugryźć iteracyjnie? Cóż…
function someIterative(arr, callback){
for(var i = 0; i < arr.length; i++){
if(callback(arr[i]))
return true;
}
return false;
}
console.log(someIterative([1,2,3], (n) => n > 10));
//false
console.log(someIterative([10,20,30], (n) => n > 10));
//true
Proste, choć wywoływanie funkcji w bloku if zazwyczaj jest bardzo złą praktyką, choć tutaj może nie aż tak.
Ogólnie jest to zła praktyka, gdy wielokrotnie wołamy funkcję zamiast raz ją wywołać, do zmiennej przypisać i potem ifami porównywać w bardziej imperatywnym programowaniu.