Zadanie
Napisz funkcję sprawdzającą, czy elementy krotki pierwszej znajdują się w krotce drugiej.
krotka1 = (3, 4, 5) krotka2 = (1, 2, 3, 4, 5, 6, 7, 8, 9)
Wbrew pozorom zadanie to wcale nie jest tak banalne jak to wygląda. Dodatkowo z uwagi na cały wachlarz możliwości w jaki można podjeść do tego problemu przyniesie ono wartość zarówno początkującym jak i tym troszkę bardziej zaawansowanym. Koniec gadania czas przejść do działania. Jak zwykle zaczniemy od rozwiązań najprostszych (nie mylić z najgorszymi), tak aby potem przejść do bardziej złożonych pomysłów.
Rozwiązanie
Tak nie robić !!!
Wielu początkujących programistów języka Python na początku próbuje zrobić coś takiego:
krotka1 = (3, 4, 5) krotka2 = (1, 2, 3, 4, 5, 6, 7, 8, 9) def czy_krotka_w_krotce(krotka1, krotka2): if krotka1 in krotka2: return True else: return False print(czy_krotka_w_krotce(krotka1, krotka2))
Dlaczego to nie zadziała ? Dodajmy, że poniższy kod działa i nawet nie zwraca błędu. Niestety nie robi on też tego czego od niego oczekiwaliśmy. Zamiast sprawdzić, czy elementy z krotka1 znajdują się w krotka2 sprawdziliśmy, czy cała krotka1 nie jest elementem krotka2. Aby powyższy warunek został spełniony krotka2 musiałaby wyglądać mniej więcej tak: krotka2 = (1, (3, 4, 5), 3, 4, 5, 6, 7, 8, 9)
Pomysł ze zliczaniem
krotka1 = (3, 4, 5) krotka2 = (1, 2, 3, 4, 5, 6, 7, 8, 9) def czy_krotka_w_krotce(krotka1, krotka2): licznik = 0 for element in krotka1: if element in krotka2: licznik += 1 if licznik == len(krotka1): return True else: return False print(czy_krotka_w_krotce(krotka1, krotka2))
Od razu powiem, że nie jest to najładniejsze podejście, a już na pewno nie jest to podejście w duchu Pythona. Mimo to działa więc warto je omówić. Na początku tworzymy licznik i przypisujemy mu wartość 1. Następnie iterujemy się po wszystkich elementach krotka1. Jeśli dany element z krotka1 znajduje się również w krotka2 wtedy zwiększamy nasz licznik o jeden. Na koniec sprawdzamy, czy wartość naszego licznika jest równa ilości elementów w krotka1. Jeśli tak to znaczy, że wszystkie elementy z krotka1 odmeldowały się również w krotka2. Jeśli wartość naszego licznika byłaby o jeden krótsza niż ilość elementów w krotka1 to znaczy, że jednego z elementów krotka1 brakuje w krotka2.
Sprawdzanie leniwe
No w końcu jakiś sposób właściwy dla mnie :). No dobra bez żartów. O co chodzi z tym lenistwem ? Otóż w poprzednim przykładzie aby stwierdzić, czy krotka1 zawiera się w krotka2 musieliśmy sprawdzić wszystkie elementy krotka1. To dlatego, że dopiero na końcu był warunek sprawdzający. Zauważmy jednak, że jeżeli chociaż jeden (np. pierwszy) element z krotka1 nie odmelduje się w krotka2 to już wiemy, że wszystkie elementy krotka1 na 100% nie znajdą się w krotka2. W takim wypadku (jako, że jesteśmy leniwi) możemy przerwać dalsze sprawdzanie i ogłosić światu wynik. Im więcej mamy danych do sprawdzenia tym bardziej opłaca nam się takie podejście.
krotka1 = (3, 4, 5) krotka2 = (1, 2, 3, 4, 5, 6, 7, 8, 9) def czy_krotka_w_krotce(krotka1, krotka2): for element in krotka1: if element not in krotka2: return False return True print(czy_krotka_w_krotce(krotka1, krotka2))
Dodatkowo nasz kod jest krótszy i łatwiejszy do zrozumienia.
Leniwy generator składany
Pewnie zastanawiałeś się czy da się nasz problem rozwiązać przy pomocy listy składanej. Oczywiście, że tak. My nawet pójdziemy o krok dalej i od razu posłużymy się generatorem. Oczywiście sam generator nie dałby pożądanego efektu. To co dzięki niemy osiągniemy to jedynie (albo aż) kolekcja składająca się z ”True” lub ”False” w zależności od tego czy udało się znaleźć konkretny element krotka1 w krotka2. Na szczęście z pomocą przychodzi funkcja ”all”. Zwraca ona ”True” jeśli wszystkie elementy kolekcji przekazanej jako parametr mają również wartość ”True”. Czyli w naszym przypadku zwróci ”True” jeśli wszystkie elementy z krotka1 były również w krotka2.
krotka1 = (3, 4, 5) krotka2 = (1, 2, 3, 4, 5, 6, 7, 8, 9) def czy_krotka_w_krotce(krotka1, krotka2): return all( True if element in krotka2 else False for element in krotka1 ) print(czy_krotka_w_krotce(krotka1, krotka2))
Dodatkowo warto pamiętać, że użycie generatora wraz z funkcją ”all” również jest leniwe. Znaczy to, że w przypadku odnotowaniu pierwszego ”False” funkcja ”all” przerywa dalsze sprawdzanie i natychmiastowo zwraca ”False” jako wynik całej operacji.
Set przychodzi z pomocą.
To rozwiązanie potraktujcie jako bonus i używajcie go z rozwagą. Tym razem najpierw kod, a potem omówienie.
krotka1 = (3, 4, 5) krotka2 = (1, 2, 3, 4, 5, 6, 7, 8, 9) def czy_krotka_w_krotce(krotka1, krotka2): return set(krotka1).issubset(krotka2) print(czy_krotka_w_krotce(krotka1, krotka2))
Co my tu zrobiliśmy? Na początek zamieniliśmy krotka1 na kolekcję typu ”set”. Następnie użyliśmy metody ”issubset” aby sprawdzić, czy nasz nowy ”set” jest podzbiorem krotka2. Wygląda prosto i schludnie. Jednak jest tu jeden niuans o którym musimy wspomnieć. Pamiętajmy, że w momencie tworzenia naszego nowego zbioru (set) niejako pozbywamy się z krotka1 wszystkich duplikatów. Zatem z krotki (1, 1, 1) po zmianie na ”set” zostałoby jedynie (1, ). Czyli jeśli interesowałoby nas czy w krotka2 znajdują się na przykład dwie wartości 2 to w ten sposób nie dało by się tego zrobić. Jeśli jednak taka informacja nas nie interesuje lub jesteśmy pewni, że nasza krotka1 nie ma powtórzeń to warto skorzystać z ”set” i jego metod.
Adam bardzo fajne zadanko 🙂 Podszedłem do tego trochę inaczej na zasadzie porównania czy obiekty Counter mają tą samą wartość, nie mylić z instancją (że to te same obiekty). Mój kod poniżej, wklejam też link do replit
https://repl.it/@SlawekM/Python25Istuple1intuple2
from collections import Counter
def check_tuples(tup1: tuple, tup2: tuple) -> bool:
counter1 = Counter(tup1)
counter2 = Counter(tup2)
return counter1 == counter2
if __name__ == „__main__”:
tup1 = (1, 2, 2, 3, 4, 4, 4)
tup2 = (4, 4, 4, 1, 2, 2, 3)
tup3 = (4, 4, 4, 1, 2, 2, 2)
tup4 = (4, 4, 4, 1, 2, 2, 3, 4)
print(check_tuples(tup1, tup2))
print(check_tuples(tup1, tup3))
print(check_tuples(tup1, tup4))
print(check_tuples(tup2, tup3))
print(check_tuples(tup2, tup4))
print(check_tuples(tup2, tup1))
Dziwnie działa edycja posta jest białe tło i tekst 🙂 trzeba użyć CTRL+a aby widzieć tekst 🙂 trzeba sobie jakoś radzić
krotka1 = (3, 4, 5,10)
krotka2 = (1, 2, 3, 4, 5, 6, 7, 8, 9)
list(map(lambda x: x in krotka2, krotka1))
Super teraz by wypadało jeszcze użyć funkcji all()