class Node: # класс решений (вопрос или конечный ответ) def __init__(self, text: str): self.text = text self.is_question = text.endswith('?') self.left = None # Ссылка на левого потомка (выбор t да) self.right = None # Ссылка на правого потомка (выбор f нет) class ZooExpertSystem: # Класс для управления системой зоопарка # Отвечает за 1чтение данных, 2построение бинарного дерева решений # 3валидацию структуры и 4проведение взаимодействия с пользователем def __init__(self): # Корень построенного бинарного дерева self.root = None # Счетчики элементов всей базы знаний self.q_count = 0 # Количество вопросов self.a_count = 0 # Количество ответов (животных) # Списки для разделенных строк входа self.db_lines = [] # Строки относящиеся к базе знаний (дереву) self.user_choices = ([]) # Строки с ответами пользователя (t или f) def run(self): # контроллер запускает программу в строгом соответствии с жизненным циклом. print("Expert zo zoo to vie.") # Считываем весь поток ввода и разделяем его if not self._parse_input(): # _parse_input говорит нам что метод приватый print("Chybna databaza") return # Строим дерево решений из прочитанных строк if not self._build_tree(): # _build_tree создает цыпочку из вопросов которые приводят к ответу да или нет print("Chybna databaza") return # Выводим информацию о базе знаний self._print_stats() # Запускаем интерактивный опрос пользователя self._run_quiz() def _read_tree_lines(self) -> list[str]: # Считывает строки базы знаний из input() до первой пустой строки tree_lines = [] try: text = input().rstrip("\r") while text != "": tree_lines.append(text) text = input().rstrip("\r") except EOFError: pass return tree_lines def _parse_input(self) -> bool: # Читаем базу знаний до пустой строки self.db_lines = self._read_tree_lines() if not self.db_lines: return False # Читаем оставшиеся строки с ответами пользователя (t / f) try: while True: choice = input().rstrip("\r") self.user_choices.append(choice) except EOFError: pass return True def _build_tree(self) -> bool: # Рекурсивная сборка бинарного дерева из db_lines в формате Preorder idx = 0 def parse_node(): nonlocal idx if idx >= len(self.db_lines): return None text = self.db_lines[idx] idx += 1 node = Node(text) if node.is_question: self.q_count += 1 # Сначала собираем левую ветку (Да / 'a'), затем правую (Нет / 'n') node.left = parse_node() node.right = parse_node() # У каждого вопроса обязательно должны быть оба потомка if node.left is None or node.right is None: return None else: self.a_count += 1 return node self.root = parse_node() # Если структура ломается или в базе остались лишние неиспользованные строки if self.root is None or idx != len(self.db_lines): return False return True