|
| 1 | +#!/usr/bin/env python3 |
| 2 | +"""Containers module example demonstrating type-safe container operations. |
| 3 | +
|
| 4 | +This example shows how to use the container wrapper for type-safe operations |
| 5 | +including filtering, transformation, and collection management. |
| 6 | +""" |
| 7 | + |
| 8 | +import sys |
| 9 | +from pathlib import Path |
| 10 | +from typing import Any, Callable, List |
| 11 | + |
| 12 | +# Add the python module to the path |
| 13 | +sys.path.insert(0, str(Path(__file__).parent.parent / 'src')) |
| 14 | + |
| 15 | +from containers import Container, create_container |
| 16 | + |
| 17 | + |
| 18 | +def basic_container_demo() -> None: |
| 19 | + """Demonstrate basic container creation and operations. |
| 20 | +
|
| 21 | + Shows fundamental container operations including creation, addition, |
| 22 | + removal, and basic iteration. |
| 23 | + """ |
| 24 | + print('=== Basic Container Demo ===') |
| 25 | + |
| 26 | + # Create containers with different types |
| 27 | + int_container = create_container([1, 3, 2, 5, 4]) |
| 28 | + str_container = Container(str, ['apple', 'banana', 'cherry']) |
| 29 | + |
| 30 | + print(f'Integer container: {list(int_container)}') |
| 31 | + print(f'String container: {list(str_container)}') |
| 32 | + |
| 33 | + # Add and remove items |
| 34 | + int_container.add(6) |
| 35 | + removed = int_container.remove(3) |
| 36 | + print(f'After adding 6 and removing 3 (removed {removed}): {list(int_container)}') |
| 37 | + |
| 38 | + # Container properties |
| 39 | + print(f'Integer container size: {len(int_container)}') |
| 40 | + print(f'Is empty: {int_container.is_empty()}') |
| 41 | + print(f'Contains 5: {int_container.contains(5)}') |
| 42 | + |
| 43 | + print() |
| 44 | + |
| 45 | + |
| 46 | +def filtering_demo() -> None: |
| 47 | + """Demonstrate container filtering capabilities. |
| 48 | +
|
| 49 | + Shows various filtering patterns and how to chain filter operations |
| 50 | + for complex data selection. |
| 51 | + """ |
| 52 | + print('=== Filtering Demo ===') |
| 53 | + |
| 54 | + # Create test data |
| 55 | + numbers = create_container(list(range(1, 21))) # 1 to 20 |
| 56 | + words = Container(str, ['apple', 'banana', 'cherry', 'date', 'elderberry', 'fig']) |
| 57 | + |
| 58 | + # Numeric filtering |
| 59 | + even_numbers = numbers.filter(lambda x: x % 2 == 0) |
| 60 | + large_numbers = numbers.filter(lambda x: x > 15) |
| 61 | + divisible_by_three = numbers.filter(lambda x: x % 3 == 0) |
| 62 | + |
| 63 | + print(f'Original numbers: {list(numbers)}') |
| 64 | + print(f'Even numbers: {even_numbers}') |
| 65 | + print(f'Large numbers (>15): {large_numbers}') |
| 66 | + print(f'Divisible by 3: {divisible_by_three}') |
| 67 | + |
| 68 | + # String filtering |
| 69 | + long_words = words.filter(lambda s: len(s) > 5) |
| 70 | + words_with_e = words.filter(lambda s: 'e' in s) |
| 71 | + words_starting_with_c = words.filter(lambda s: s.startswith('c')) |
| 72 | + |
| 73 | + print(f'\nOriginal words: {list(words)}') |
| 74 | + print(f'Long words (>5 chars): {long_words}') |
| 75 | + print(f'Words containing "e": {words_with_e}') |
| 76 | + print(f'Words starting with "c": {words_starting_with_c}') |
| 77 | + |
| 78 | + # Complex filtering with multiple conditions |
| 79 | + complex_filter = numbers.filter(lambda x: x % 2 == 0 and x > 10) |
| 80 | + print(f'Even numbers > 10: {complex_filter}') |
| 81 | + |
| 82 | + print() |
| 83 | + |
| 84 | + |
| 85 | +def transformation_demo() -> None: |
| 86 | + """Demonstrate container transformation capabilities. |
| 87 | +
|
| 88 | + Shows how to transform container elements using mapping functions |
| 89 | + and create new containers with modified data. |
| 90 | + """ |
| 91 | + print('=== Transformation Demo ===') |
| 92 | + |
| 93 | + # Numeric transformations |
| 94 | + numbers = create_container([1, 2, 3, 4, 5]) |
| 95 | + |
| 96 | + squared = numbers.transform(lambda x: x * x) |
| 97 | + doubled = numbers.transform(lambda x: x * 2) |
| 98 | + negated = numbers.transform(lambda x: -x) |
| 99 | + |
| 100 | + print(f'Original: {list(numbers)}') |
| 101 | + print(f'Squared: {squared}') |
| 102 | + print(f'Doubled: {doubled}') |
| 103 | + print(f'Negated: {negated}') |
| 104 | + |
| 105 | + # String transformations |
| 106 | + words = Container(str, ['hello', 'world', 'python', 'container']) |
| 107 | + |
| 108 | + lengths = words.transform(len) |
| 109 | + uppercase = words.transform(str.upper) |
| 110 | + reversed_words = words.transform(lambda s: s[::-1]) |
| 111 | + first_chars = words.transform(lambda s: s[0] if s else '') |
| 112 | + |
| 113 | + print(f'\nOriginal words: {list(words)}') |
| 114 | + print(f'Word lengths: {lengths}') |
| 115 | + print(f'Uppercase: {uppercase}') |
| 116 | + print(f'Reversed: {reversed_words}') |
| 117 | + print(f'First characters: {first_chars}') |
| 118 | + |
| 119 | + # Complex transformations |
| 120 | + numbers_to_strings = numbers.transform(lambda x: f'Number: {x}') |
| 121 | + print(f'Complex transformation: {numbers_to_strings}') |
| 122 | + |
| 123 | + print() |
| 124 | + |
| 125 | + |
| 126 | +def chained_operations_demo() -> None: |
| 127 | + """Demonstrate chaining of container operations. |
| 128 | +
|
| 129 | + Shows how to combine filtering and transformation operations |
| 130 | + to create complex data processing pipelines. |
| 131 | + """ |
| 132 | + print('=== Chained Operations Demo ===') |
| 133 | + |
| 134 | + # Create sample data |
| 135 | + data = create_container(list(range(1, 11))) |
| 136 | + print(f'Original data: {list(data)}') |
| 137 | + |
| 138 | + # Chain operations step by step |
| 139 | + step1 = data.filter(lambda x: x % 2 == 1) # Keep odd numbers |
| 140 | + step2 = step1.transform(lambda x: x * x) # Square them |
| 141 | + step3 = Container(int, step2).filter(lambda x: x < 50) # Keep those < 50 |
| 142 | + |
| 143 | + print(f'Step 1 - Odd numbers: {step1}') |
| 144 | + print(f'Step 2 - Squared: {step2}') |
| 145 | + print(f'Step 3 - Squares < 50: {step3}') |
| 146 | + |
| 147 | + # Working with strings - pipeline processing |
| 148 | + text_data = Container( |
| 149 | + str, ['The', 'quick', 'brown', 'fox', 'jumps', 'over', 'lazy', 'dog'] |
| 150 | + ) |
| 151 | + |
| 152 | + # Process: keep words > 3 chars, convert to uppercase, get lengths |
| 153 | + processed_text = text_data.filter(lambda w: len(w) > 3) |
| 154 | + upper_text = Container(str, processed_text).transform(str.upper) |
| 155 | + final_lengths = Container(str, upper_text).transform(len) |
| 156 | + |
| 157 | + print(f'\nText processing pipeline:') |
| 158 | + print(f'Original: {list(text_data)}') |
| 159 | + print(f'Words > 3 chars: {processed_text}') |
| 160 | + print(f'Uppercase: {upper_text}') |
| 161 | + print(f'Final lengths: {final_lengths}') |
| 162 | + |
| 163 | + print() |
| 164 | + |
| 165 | + |
| 166 | +def type_safety_demo() -> None: |
| 167 | + """Demonstrate type safety features of containers. |
| 168 | +
|
| 169 | + Shows how containers maintain type safety and provide |
| 170 | + compile-time and runtime type checking capabilities. |
| 171 | + """ |
| 172 | + print('=== Type Safety Demo ===') |
| 173 | + |
| 174 | + # Homogeneous containers |
| 175 | + int_container = Container(int, [1, 2, 3]) |
| 176 | + str_container = Container(str, ['a', 'b', 'c']) |
| 177 | + float_container = Container(float, [1.1, 2.2, 3.3]) |
| 178 | + |
| 179 | + print(f'Integer container: {list(int_container)}') |
| 180 | + print(f'String container: {list(str_container)}') |
| 181 | + print(f'Float container: {list(float_container)}') |
| 182 | + |
| 183 | + # Type-specific operations |
| 184 | + print(f'\nType-specific operations:') |
| 185 | + |
| 186 | + # Integer operations |
| 187 | + int_sum = sum(int_container) |
| 188 | + int_max = max(int_container) |
| 189 | + print(f'Integer sum: {int_sum}, max: {int_max}') |
| 190 | + |
| 191 | + # String operations |
| 192 | + str_joined = ' '.join(str_container) |
| 193 | + str_total_length = sum(len(s) for s in str_container) |
| 194 | + print(f'Joined strings: "{str_joined}", total length: {str_total_length}') |
| 195 | + |
| 196 | + # Float operations with precision |
| 197 | + float_avg = sum(float_container) / len(float_container) |
| 198 | + print(f'Float average: {float_avg:.3f}') |
| 199 | + |
| 200 | + # Demonstrate type preservation in transformations |
| 201 | + int_squared = int_container.transform(lambda x: x * x) |
| 202 | + str_lengths = str_container.transform(len) |
| 203 | + float_rounded = float_container.transform(lambda x: round(x, 1)) |
| 204 | + |
| 205 | + print(f'\nType-preserving transformations:') |
| 206 | + print(f'Integer squared: {int_squared}') |
| 207 | + print(f'String lengths: {str_lengths}') |
| 208 | + print(f'Float rounded: {float_rounded}') |
| 209 | + |
| 210 | + print() |
| 211 | + |
| 212 | + |
| 213 | +def advanced_operations_demo() -> None: |
| 214 | + """Demonstrate advanced container operations. |
| 215 | +
|
| 216 | + Shows complex use cases including batch processing, conditional |
| 217 | + operations, and advanced filtering patterns. |
| 218 | + """ |
| 219 | + print('=== Advanced Operations Demo ===') |
| 220 | + |
| 221 | + # Batch processing with multiple containers |
| 222 | + datasets = [ |
| 223 | + create_container([1, 2, 3, 4, 5]), |
| 224 | + create_container([6, 7, 8, 9, 10]), |
| 225 | + create_container([11, 12, 13, 14, 15]), |
| 226 | + ] |
| 227 | + |
| 228 | + print('Batch processing multiple containers:') |
| 229 | + for i, dataset in enumerate(datasets, 1): |
| 230 | + processed = dataset.filter(lambda x: x % 2 == 0).transform(lambda x: x * 2) |
| 231 | + print(f' Dataset {i}: {list(dataset)} -> {processed}') |
| 232 | + |
| 233 | + # Conditional operations |
| 234 | + mixed_data = create_container([-3, -1, 0, 2, 5, 8, 12]) |
| 235 | + |
| 236 | + # Separate positive and negative numbers |
| 237 | + positive = mixed_data.filter(lambda x: x > 0) |
| 238 | + negative = mixed_data.filter(lambda x: x < 0) |
| 239 | + zero_or_positive = mixed_data.filter(lambda x: x >= 0) |
| 240 | + |
| 241 | + print(f'\nConditional separation:') |
| 242 | + print(f'Original: {list(mixed_data)}') |
| 243 | + print(f'Positive: {positive}') |
| 244 | + print(f'Negative: {negative}') |
| 245 | + print(f'Zero or positive: {zero_or_positive}') |
| 246 | + |
| 247 | + # Statistical operations |
| 248 | + stats_data = create_container([1, 2, 2, 3, 4, 4, 4, 5, 6]) |
| 249 | + |
| 250 | + unique_values = list(set(stats_data)) |
| 251 | + value_counts = {val: list(stats_data).count(val) for val in unique_values} |
| 252 | + |
| 253 | + print(f'\nStatistical analysis:') |
| 254 | + print(f'Data: {list(stats_data)}') |
| 255 | + print(f'Unique values: {sorted(unique_values)}') |
| 256 | + print(f'Value counts: {value_counts}') |
| 257 | + |
| 258 | + # Find most frequent value |
| 259 | + most_frequent = max(value_counts.items(), key=lambda x: x[1]) |
| 260 | + print(f'Most frequent value: {most_frequent[0]} (appears {most_frequent[1]} times)') |
| 261 | + |
| 262 | + print() |
| 263 | + |
| 264 | + |
| 265 | +def performance_demo() -> None: |
| 266 | + """Demonstrate performance characteristics of container operations. |
| 267 | +
|
| 268 | + Shows how different operations scale and provides insights into |
| 269 | + performance considerations for large datasets. |
| 270 | + """ |
| 271 | + print('=== Performance Demo ===') |
| 272 | + |
| 273 | + # Create larger datasets for performance testing |
| 274 | + small_data = create_container(list(range(100))) |
| 275 | + medium_data = create_container(list(range(1000))) |
| 276 | + large_data = create_container(list(range(10000))) |
| 277 | + |
| 278 | + datasets = [ |
| 279 | + ('Small (100 elements)', small_data), |
| 280 | + ('Medium (1000 elements)', medium_data), |
| 281 | + ('Large (10000 elements)', large_data), |
| 282 | + ] |
| 283 | + |
| 284 | + print('Performance comparison across dataset sizes:') |
| 285 | + |
| 286 | + for name, dataset in datasets: |
| 287 | + # Time filtering operation |
| 288 | + import time |
| 289 | + |
| 290 | + start_time = time.perf_counter() |
| 291 | + filtered = dataset.filter(lambda x: x % 10 == 0) |
| 292 | + filter_time = time.perf_counter() - start_time |
| 293 | + |
| 294 | + start_time = time.perf_counter() |
| 295 | + transformed = dataset.transform(lambda x: x * 2) |
| 296 | + transform_time = time.perf_counter() - start_time |
| 297 | + |
| 298 | + print(f' {name}:') |
| 299 | + print(f' Filter time: {filter_time:.6f}s, result size: {len(filtered)}') |
| 300 | + print( |
| 301 | + f' Transform time: {transform_time:.6f}s, result size: {len(transformed)}' |
| 302 | + ) |
| 303 | + |
| 304 | + # Memory efficiency demonstration |
| 305 | + print(f'\nMemory efficiency:') |
| 306 | + efficient_chain = large_data.filter(lambda x: x % 100 == 0).transform( |
| 307 | + lambda x: x // 100 |
| 308 | + ) |
| 309 | + print( |
| 310 | + f'Chained operations on large dataset: {len(efficient_chain)} elements processed' |
| 311 | + ) |
| 312 | + |
| 313 | + print() |
| 314 | + |
| 315 | + |
| 316 | +def main() -> int: |
| 317 | + """Run all container examples. |
| 318 | +
|
| 319 | + Returns |
| 320 | + ------- |
| 321 | + int |
| 322 | + Exit code (0 for success, 1 for error) |
| 323 | + """ |
| 324 | + print('Containers Module Example') |
| 325 | + print('========================') |
| 326 | + print() |
| 327 | + |
| 328 | + try: |
| 329 | + basic_container_demo() |
| 330 | + filtering_demo() |
| 331 | + transformation_demo() |
| 332 | + chained_operations_demo() |
| 333 | + type_safety_demo() |
| 334 | + advanced_operations_demo() |
| 335 | + performance_demo() |
| 336 | + |
| 337 | + print('All container examples completed successfully!') |
| 338 | + return 0 |
| 339 | + |
| 340 | + except Exception as e: |
| 341 | + print(f'Error running container examples: {e}') |
| 342 | + import traceback |
| 343 | + |
| 344 | + traceback.print_exc() |
| 345 | + return 1 |
| 346 | + |
| 347 | + |
| 348 | +if __name__ == '__main__': |
| 349 | + sys.exit(main()) |
0 commit comments