Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ЛР-7 / ЯП №7.docx
Скачиваний:
2
Добавлен:
06.06.2026
Размер:
387.03 Кб
Скачать

Министерство науки и высшего образования Российской Федерации

Федеральное государственное автономное образовательное учреждение

высшего образования

ТОМСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ СИСТЕМ УПРАВЛЕНИЯ И РАДИОЭЛЕКТРОНИКИ (ТУСУР)

Кафедра безопасности информационных систем (БИС)

ФУНКЦИОНАЛЬНЫЙ СТИЛЬ ПРОГРАММИРОВАНИЯ. ПАРАЛЛЕЛЬНОЕ ПРОГРАММИРОВАНИЕ

Отчет по лабораторной работе №7

по дисциплине «Языки программирования»

Студент гр.

_______

_______

Принял:

Доцент каф. КИБЭВС, д.т.н., доцент

_______ Романов А. С.

_______

Оглавление

Введение 3

Ход работы 4

Заключение 5

Приложение А 6

Введение

Цель работы – реализовать систему, где:

1. Данные хранятся в неизменяемых структурах.

2. Вся обработка идет через цепочку чистых функций и лямбд с помощьюя

Map/Filter/Reduce.

3. Ошибки и пустые данные обрабатываются через кастомную монаду Maybe / Either.

4. Настройки и фильтры задаются через каррирование.

5. Сложные вложенные структуры обходятся с помощью рекурсии.

6. Вычисления распараллеливаются через чистый Map/Reduce. Нарезка данных, параллельный маппинг воркерами на уровне процессов ОС и финальная свертка результатов должны выполняться без риска взаимных блокировок благодаря полной чистоте функций. Код написан на ЯП Python для 19 варианта.

Ход работы

В ходе работы была реализована система с помощью кода (приложение А), по заданной структуре, проведен демонстрационный вывод (рисунок 1.1).

Рисунок 1.1 – Демонстрационный вывод

Заключение

В ходе работы была реализована система, удовлетворяющая всем пунктам структуры для ИЗ по вариантом 19, на ЯП Python.

Приложение а

(обязательное)

Код реализации системы

from dataclasses import dataclass, replace from typing import Callable, List, Tuple, Optional, Any, NamedTuple from functools import reduce from multiprocessing import Pool, cpu_count import random # 1. ИММУТАБЕЛЬНЫЕ СТРУКТУРЫ ДАННЫХ @dataclass(frozen=True) class GeoCoord: #неизменяемые географические координаты фонаря lat: float lon: float @dataclass(frozen=True) class SunSchedule: #неизменяемый график восхода/захода солнца sunrise_hour: float sunset_hour: float @dataclass(frozen=True) class LampConfig: #неизменяемая конфигурация фонаря lamp_id: str location: GeoCoord max_lumens: int power_watts: float sun_schedule: SunSchedule @dataclass(frozen=True) class SensorReading: #неизменяемое показание датчика lamp_id: str timestamp: float lux: float is_raining: bool @dataclass(frozen=True) class LampState: #неизменяемое состояние фонаря (результат вычислений) lamp_id: str brightness_percent: float power_consumption: float is_emergency: bool # 2. МОНАДА MAYBE (обработка ошибок) class Maybe: #монада Maybe для безопасной работы с отсутствующими данными def __init__(self, value: Any): self._value = value def bind(self, func: Callable[[Any], 'Maybe']) -> 'Maybe': #монадическое связывание (flatMap) if self._value is None: return Maybe(None) return func(self._value) def map(self, func: Callable[[Any], Any]) -> 'Maybe': #функтор map if self._value is None: return Maybe(None) try: return Maybe(func(self._value)) except Exception: return Maybe(None) def get_or_else(self, default: Any) -> Any: return self._value if self._value is not None else default def is_nothing(self) -> bool: return self._value is None def is_just(self) -> bool: return self._value is not None def __repr__(self): return f"Just({self._value})" if self.is_just() else "Nothing" # 3. ЧИСТЫЕ ФУНКЦИИ (глобальные для сериализации в multiprocessing) def calculate_brightness(lux: float, is_raining: bool, sunset_hour: float, timestamp: float, max_lumens: int) -> float: #чистая функция: вычисление процента яркости светодиодной лампы в зависимости от уровня естественного света. if lux < 0: lux = 0 base_brightness = max(0, min(100, 100 - (lux / 10))) rain_multiplier = 1.3 if is_raining else 1.0 hours_after_sunset = max(0, timestamp - sunset_hour) night_boost = 1.0 + (hours_after_sunset * 0.1) final_brightness = base_brightness * rain_multiplier * night_boost return min(100, final_brightness) def calculate_power_consumption(brightness_percent: float, max_power: float) -> float: #чистая функция: расчёт потребляемой мощности return (brightness_percent / 100) * max_power # 4. ЛЯМБДА-ФУНКЦИИ (фильтры) CRITICAL_DARK = 5.0 is_emergency_dark = lambda reading: reading.lux < CRITICAL_DARK is_night_time = lambda reading: reading.timestamp < 7 or reading.timestamp > 19 is_critical_post = lambda reading: reading.lux < CRITICAL_DARK # 5. ФУНКЦИЯ ВЫСШЕГО ПОРЯДКА (ФВП) def manage_grid(lux_filter: Callable[[SensorReading], bool], dimming_strategy: Callable[[List[LampState]], List[LampState]], lamps: Tuple[LampConfig, ...], readings: Tuple[SensorReading, ...]) -> List[LampState]: #ФВП: контроллер энергосбережения принимает функцию-фильтр и функцию-стратегию диммирования. filtered_readings = list(filter(lux_filter, readings)) lamp_states = [] for reading in filtered_readings: lamp_config = next( (l for l in lamps if l.lamp_id == reading.lamp_id), None ) if lamp_config: brightness = calculate_brightness( reading.lux, reading.is_raining, lamp_config.sun_schedule.sunset_hour, reading.timestamp, lamp_config.max_lumens ) power = calculate_power_consumption(brightness, lamp_config.power_watts) state = LampState( lamp_id=reading.lamp_id, brightness_percent=brightness, power_consumption=power, is_emergency=reading.lux < CRITICAL_DARK ) lamp_states.append(state) return dimming_strategy(lamp_states) # 6. КАРРИРОВАНИЕ (Currying) def apply_holiday_profile(profile_name: str): #каррированная функция: фиксация праздничного режима def with_dimming_curve(dimming_curve: str): def with_lamp_id(lamp_id: str) -> dict: return { "profile": profile_name, "curve": dimming_curve, "lamp": lamp_id, "brightness_override": 80 if profile_name == "New_Year" else 100 } return with_lamp_id return with_dimming_curve # 7. РЕКУРСИЯ (обход вложенных структур) @dataclass(frozen=True) class PowerLineNode: #узел электропередачи (для каскадного отключения) node_id: str children: Tuple['PowerLineNode', ...] = () is_active: bool = True def cascade_disconnect(node: PowerLineNode, fault_node_id: str) -> PowerLineNode: #рекурсивная функция: каскадное отключение линий электропередачи при обнаружении короткого замыкания. if node.node_id == fault_node_id: return replace(node, is_active=False, children=tuple( replace(child, is_active=False) for child in node.children )) new_children = tuple( cascade_disconnect(child, fault_node_id) for child in node.children ) any_child_disabled = any(not c.is_active for c in new_children) return replace(node, children=new_children, is_active=not any_child_disabled) # 8. REDUCE (свёртка) def reduce_grid_consumption(states: List[LampState]) -> dict: # Reduce: cжатие данных о потреблении всей сети фонарей в итоговую метрику. if not states: return {"total_kwh": 0, "avg_brightness": 0, "emergency_count": 0} total_power = reduce(lambda acc, s: acc + s.power_consumption, states, 0) avg_brightness = reduce(lambda acc, s: acc + s.brightness_percent, states, 0) / len(states) emergency_count = reduce(lambda acc, s: acc + (1 if s.is_emergency else 0), states, 0) total_kwh = total_power / 1000 return { "total_kwh": total_kwh, "avg_brightness": avg_brightness, "emergency_count": emergency_count, "savings_percent": max(0, 100 - avg_brightness) } # 9. ПАРАЛЛЕЛЬНЫЙ MAP/REDUCE class WorkerInput(NamedTuple): #иммутабельный вход для воркера readings: List[SensorReading] lamps: List[LampConfig] def process_chunk_worker(worker_input: WorkerInput) -> List[LampState]: #чистая глобальная функция-маппер для воркера. results = [] lamps_dict = {l.lamp_id: l for l in worker_input.lamps} for reading in worker_input.readings: lamp = lamps_dict.get(reading.lamp_id) if lamp: brightness = calculate_brightness( reading.lux, reading.is_raining, lamp.sun_schedule.sunset_hour, reading.timestamp, lamp.max_lumens ) power = calculate_power_consumption(brightness, lamp.power_watts) results.append(LampState( lamp_id=reading.lamp_id, brightness_percent=brightness, power_consumption=power, is_emergency=reading.lux < CRITICAL_DARK )) return results def parallel_process_lamps(lamps: Tuple[LampConfig, ...], readings: Tuple[SensorReading, ...], num_workers: Optional[int] = None) -> List[LampState]: #параллельная обработка через чистый Map/Reduce if num_workers is None: num_workers = min(cpu_count(), 4) readings_list = list(readings) lamps_list = list(lamps) chunk_size = max(1, len(readings_list) // num_workers) chunks = [ WorkerInput(readings_list[i:i + chunk_size], lamps_list) for i in range(0, len(readings_list), chunk_size) ] with Pool(num_workers) as pool: mapped_results = pool.map(process_chunk_worker, chunks) all_states = reduce(lambda acc, chunk: acc + chunk, mapped_results, []) return all_states # ГЕНЕРАЦИЯ ТЕСТОВЫХ ДАННЫХ def generate_test_data(num_lamps: int = 100) -> Tuple[Tuple[LampConfig, ...], Tuple[SensorReading, ...]]: #чистая функция генерации тестовых данных random.seed(42) lamps = tuple( LampConfig( f"lamp_{i:03d}", GeoCoord(56.0 + i * 0.001, 84.0 + i * 0.001), 5000 + (i % 3) * 1000, 50.0 + (i % 3) * 10, SunSchedule(6.0 + (i % 2) * 0.5, 19.0 + (i % 2) * 0.5) ) for i in range(num_lamps) ) readings = tuple( SensorReading( f"lamp_{i:03d}", 22.0 if i % 3 == 0 else 14.0, random.uniform(0.5, 200.0), random.choice([True, False]) ) for i in range(num_lamps) ) return lamps, readings # ДЕМОНСТРАЦИЯ РАБОТЫ СИСТЕМЫ def run_demo(): #базовые данные lamps = ( LampConfig("lamp_001", GeoCoord(56.49, 84.98), 5000, 50.0, SunSchedule(6.5, 19.5)), LampConfig("lamp_002", GeoCoord(56.50, 84.99), 6000, 60.0, SunSchedule(6.5, 19.5)), LampConfig("lamp_003", GeoCoord(56.51, 85.00), 4500, 45.0, SunSchedule(6.5, 19.5)), LampConfig("lamp_004", GeoCoord(56.52, 85.01), 7000, 70.0, SunSchedule(6.5, 19.5)), LampConfig("lamp_005", GeoCoord(56.53, 85.02), 5500, 55.0, SunSchedule(6.5, 19.5)), ) readings = ( SensorReading("lamp_001", 22.0, 2.0, False), SensorReading("lamp_002", 22.0, 150.0, False), SensorReading("lamp_003", 22.0, 1.0, True), SensorReading("lamp_004", 14.0, 800.0, False), SensorReading("lamp_005", 22.0, 3.0, False), ) print("\n1. ИММУТАБЕЛЬНЫЕ ДАННЫЕ:") print(f" Лампы: {len(lamps)} шт. (frozen dataclass)") print(f" Показания: {len(readings)} шт. (frozen dataclass)") print(f" Попытка изменения вызовет AttributeError") print("\n2. МОНАДА MAYBE (обработка сбоя датчика):") sensor_broken = Maybe(None) sensor_ok = Maybe(150.0) print(f" Сбой датчика: {sensor_broken} -> яркость: {sensor_broken.map(lambda x: calculate_brightness(x, False, 19.5, 22.0, 5000)).get_or_else(50.0)}%") print(f" Нормальный: {sensor_ok} -> яркость: {sensor_ok.map(lambda x: calculate_brightness(x, False, 19.5, 22.0, 5000)).get_or_else(50.0)}%") print("\n3. ЛЯМБДА-ФИЛЬТРЫ:") print(f" Критически темные (< {CRITICAL_DARK} lux): {[r.lamp_id for r in filter(is_critical_post, readings)]}") print(f" Ночные посты: {[r.lamp_id for r in filter(is_night_time, readings)]}") print("\n4. ФВП manage_grid + стратегия диммирования:") def energy_save_strategy(states: List[LampState]) -> List[LampState]: return [replace(s, brightness_percent=s.brightness_percent * 0.8, power_consumption=s.power_consumption * 0.8) for s in states] grid_states = manage_grid( lambda r: r.lux < 200, energy_save_strategy, lamps, readings ) for s in grid_states: print(f" {s.lamp_id}: {s.brightness_percent:.1f}% | {s.power_consumption:.1f}Вт | emergency={s.is_emergency}") print("\n5. REDUCE (свёртка метрик):") metrics = reduce_grid_consumption(grid_states) for k, v in metrics.items(): print(f" {k}: {v:.4f}" if isinstance(v, float) else f" {k}: {v}") print("\n6. РЕКУРСИЯ (каскадное отключение электросети):") line_tree = PowerLineNode("main", ( PowerLineNode("sub_1", (PowerLineNode("line_1"), PowerLineNode("line_2"))), PowerLineNode("sub_2", (PowerLineNode("line_3"), PowerLineNode("line_4", (PowerLineNode("line_5"),)))), )) def tree_to_str(node, level=0): lines = [" " * level + f"{node.node_id}: active={node.is_active}"] for c in node.children: lines.extend(tree_to_str(c, level + 1)) return lines print(" До:") print("\n".join(tree_to_str(line_tree))) disabled = cascade_disconnect(line_tree, "line_4") print(" После отключения line_4:") print("\n".join(tree_to_str(disabled))) print("\n7. ПАРАЛЛЕЛЬНЫЙ MAP/REDUCE (100 ламп, 4 воркера):") big_lamps, big_readings = generate_test_data(100) parallel_result = parallel_process_lamps(big_lamps, big_readings, num_workers=4) parallel_metrics = reduce_grid_consumption(parallel_result) print(f" Обработано ламп: {len(parallel_result)}") print(f" Потребление: {parallel_metrics['total_kwh']:.4f} кВт·ч") print(f" Средняя яркость: {parallel_metrics['avg_brightness']:.2f}%") print(f" Аварийных: {parallel_metrics['emergency_count']}") print("\n8. КАРРИРОВАНИЕ (праздничные профили):") ny = apply_holiday_profile("New_Year") print(f" {ny('soft')('lamp_001')}") print(f" {ny('bright')('lamp_002')}") if __name__ == '__main__': run_demo()

Томск 2026

Соседние файлы в папке ЛР-7