Kolejne ciekawe ćwiczenie rekurencyjne – piszemy program, który liczy ilość cyfr 0 w liczbie. Do dzieła.

Pierwsze rozwiązanie:

function countZeros(num){
    if(num === 0)
        return 0;

    let lastDigit = num % 10;
    if(lastDigit === 0)
        return 1 + countZeros(Math.floor(num / 10));
    return 0 + countZeros(Math.floor(num / 10));
}

0 to base-case (czyli to, co zostanie po obcinaniu cyfr poprzez dzielenie bez reszty przez 10). Wyciąganie ostatniej liczby też chyba jest jasne, podobnie jak wywołanie rekurencyjne z 1 lub 0 + pomniejszona liczba.

Co najwyżej jeden edge-case się rysuje:

console.log(countZeros(10230010)); //4
console.log(countZeros(0)); //0

Pytanie jak do tego podchodzimy. Czy 0 ma 0 zer? W sensie 10 ma jedno ale w 10 to zero coś znaczy. Moim zdaniem ilość zer to ilość zer i trzeba to zrobić porządnie, ale tak, aby nie zepsuć base-case. Czyli:

function countZeros_v2(n){
    
    if(n === 0)
        return 1;

    function _countZeros(num){
        if(num === 0)
            return 0;

        let lastDigit = num % 10;
        if(lastDigit === 0)
            return 1 + _countZeros(Math.floor(num / 10));
        return 0 + _countZeros(Math.floor(num / 10));
    }

    return _countZeros(n);
    
}

console.log(countZeros_v2(0)); //1
console.log(countZeros_v2(10)); //1
console.log(countZeros_v2(1000)); //3

I tak w przypadku 0 jako argumentu przekazanego bezpośrednio poprzez fasadę (czyli chcemy zwracać ilość 0 w zerze) zwracamy 1, zaś gdy funkcja sama siebie woła z 0 to (np. w 10 dodała że jest zero, podzieliła przez 10, 1 nie ma zera, podzieliła przez 10 i ma zero) to zwracamy 0 bo pora kończyć, już że 10 ma 1 mamy zapisane.