﻿import time

def zadanie_4():
    """
    Prezentacja łańcuchowania generatorów (Pipeline / potoki danych).
    Pozwala na przetwarzanie dużych zbiorów danych "w locie", minimalizując zużycie pamięci.
    """
    
    print("Konfiguracja potoku danych...")
    
    # Etap 1: Wyrażenie generatorowe obliczające kwadraty liczb
    # (Wykorzystuje nawiasy okrągłe zamiast kwadratowych - stąd wynik nie jest w pamięci RAM)
    kwadraty = (x * x for x in range(1000000))
    
    # Etap 2: Filtracja - wybieramy tylko kwadraty podzielne przez 5
    podzielne = (n for n in kwadraty if n % 5 == 0)
    
    # Etap 3: Formatowanie danych
    wyniki = (f"Liczba: {n}" for n in podzielne)
    
    print("Potok skonfigurowany. Obliczenia jeszcze się nie rozpoczęły!\n")
    
    # Ręczne pobranie kilku wyników z potoku za pomocą funkcji next()
    print("Pobieranie 2 pierwszych elementów z potoku (funkcja next()):")
    print(next(wyniki)) # Wygeneruje 0 (0*0 = 0 % 5 == 0)
    print(next(wyniki)) # Następny element to 25 (5*5 = 25 % 5 == 0)
    
    # Czas pomiaru dla przetworzenia pewnej ilości wyników
    print("\nPobieranie kolejnych 5 elementów za pomocą pętli for:")
    start_time = time.time()
    for _ in range(5):
        # Pobieramy kolejne wyniki z generatora wyniki
        # Zauważ że nie resetuje się on, stan potoku jest kontynuowany
        print(next(wyniki))
    end_time = time.time()
    
    print(f"\nCzas wykonania pobrania 5 elementów: {end_time - start_time:.6f} sek.")
    print("Dowód: Obliczenia wykonywane są 'leniwie' na żądanie, bez przetwarzania od razu 1000000 liczb.")

if __name__ == "__main__":
    zadanie_4()
