فهرست مقاله
در بازبینیِ یک مسیرِ پردازشِ داده، تعدادِ خروجیها همیشه نشان نمیدهد چند عضو از منبع خوانده شدهاند. ممکن است برای برداشتنِ دو نمونه، چند نمونهٔ دیگر نیز از یک تکرارگر عبور کرده باشند؛ مرحلهٔ بعد دیگر از ابتدای آن شروع نمیکند. این درس رفتارِ واقعیِ itertools.islice را روی یک قابِ کوچک و ساختگی مشاهده میکند. خروجیِ انتخابشده، مرزِ مصرفِ موفق و تلاشِ رسیدن به پایان را جدا ثبت میکنیم. هیچ فایل، حسگر، شبکه، ساعت یا پردازشِ همزمانی در مثال وجود ندارد.
۱. خروجیِ برش را از ورودیِ مصرفشده جدا کنیم

قابِ اصلی هشت مقدار دارد: ۱۰۱، ۱۰۳، ۱۰۷، ۱۰۹، ۱۱۳، ۱۲۷، ۱۳۱ و ۱۳۷. آنها شمارشهایِ صحیح و تألیفیِ ADC هستند؛ واحدِ ولتاژ، زمان و مشخصاتِ دستگاه برایشان تعریف نکردهایم. جایگاههایِ صفر تا هفت، ترتیبِ همین تاپل را مشخص میکنند. شمارهٔ جایگاه و شمارِ درخواستها نیز تعدادِ بیبُعدِ رکوردند. از اختلافِ جایگاهها، تأخیر یا نرخِ نمونهبرداریِ واقعی نتیجه نمیگیریم.
طبقِ قراردادِ رسمیِ islice، این ابزار اعضایِ انتخابیِ یک ورودی را به شکلِ تکرارگر میدهد. در برشِ شروعِ یک، توقفِ هفت و گامِ سه، جایگاههایِ یک و چهار انتخاب میشوند؛ پاسخِ دستی ۱۰۳ و ۱۱۳ است. برای رسیدن به آنها، منبع باید از اعضایِ پیشین و بینشان عبور کند. گامِ سه یعنی فاصلهٔ انتخابِ خروجیها؛ خواندنِ مستقیمِ تصادفی از یک دستگاه را فراهم نمیکند.
وقتی همین برش را تا پایان مصرف کنیم، منبع هفت مقدارِ نخست را تحویل داده است. جایگاههایِ پنج و شش نیز برای رسیدن به مرزِ توقف رد میشوند، هرچند در خروجی نیستند. تنها مقدارِ ۱۳۷ باقی میماند. بنابراین دو خروجی را نمیتوان دو برداشتِ موفقِ منبع نامید. این تفاوت در ادامهدادنِ یک پیمایش مهم است: مرحلهٔ بعد از جایگاهِ فعلی میخواند و اعضایِ ردشده خودکار بازپخش نمیشوند.
در درسِ دو مصرفکنندهٔ tee، جایگاههایِ مستقل میتوانستند یک مقدارِ قبلاً گرفتهشده را دوباره تحویل بگیرند. اینجا یک منبع و یک برش داریم؛ مسیرِ بازپخش یا بافرِ دو مصرفکننده ساخته نشده است. سؤالِ تازه، رابطهٔ تنظیماتِ برش و پیشوندِ مصرفشده است. حفاظتِ موردِ نظر، آشکارکردنِ همین اثر در بازبینیِ کد است؛ منبعِ دلخواه و آثارِ بیرونی در دامنهٔ تابع نیستند.
۲. تخلیهٔ کامل با برداشتنِ چند خروجی فرق دارد

تابعِ trace_slice یک حدِ درخواستِ خروجی هم میگیرد. مقدارِ None یعنی تا دریافتِ پایانِ خودِ برش ادامه بدهیم. حدِ صحیحِ دو یعنی حداکثر دو بار خروجی بخواهیم و پس از پاسخِ دوم متوقف شویم؛ درخواستِ اضافهای برای اثباتِ پایان انجام نمیدهیم. این حد، سیاستِ آزمایشِ ماست و پارامترِ تازهای برای خودِ islice معرفی نمیشود.
با حدِ دو، همان ۱۰۳ و ۱۱۳ برمیگردند، اما منبع فقط پنج مقدارِ نخست را تحویل داده است. دنبالهٔ باقیمانده اکنون ۱۲۷، ۱۳۱ و ۱۳۷ است. پاسخِ انتخابی با تخلیهٔ کامل برابر است، ولی وضعیتِ ادامه برابر نیست. درخواستِ بعدی از خودِ برش، اعضایِ پنج و شش را رد میکرد و سپس پایان میداد. وقتی آن درخواست را نکردهایم، نباید مرزِ هفت را به گزارش نسبت بدهیم.
ستونِ مشاهدهٔ پایان نیز دقیقاً همین نکته را ثبت میکند. تمامشدنِ بودجهٔ درخواست، الزاماً مشاهدهٔ StopIteration نیست. حتی اگر آخرین مقدارِ منبع درست در آخرین درخواست تحویل شده باشد، ممکن است هنوز پایان را نخواسته باشیم. برنامه، این دو حالت را با یک پرچمِ مبهمِ «همهچیز تمام شد» جایگزین نمیکند. گزارش، snapshot همان لحظه است و برای دانستنِ دمِ باقیمانده، تکرارگر را جدا تخلیه نمیکند.
دمِ ورودی از روی تاپلِ تغییرناپذیر و شمارِ برداشتهایِ موفق ساخته میشود. این امکان، ابزارِ مشاهدهٔ همین آزمایشِ محدود است. در مسیرِ واقعیِ یکبارمصرف، داشتنِ فهرستِ قبلیِ تمامِ اعضا فرضِ بزرگی است؛ کدِ حاضر خواندنِ فایل یا بازگرداندنِ حسگر را پیاده نکرده است. برابر بودنِ مقادیرِ دو گزارش، بهتنهایی برای تصمیمِ استفادهٔ دوباره از منبع کافی نیست.
۳. برشِ تهی هم میتواند منبع را جلو ببرد

برشِ شروعِ پنج و توقفِ دو هیچ عضوی انتخاب نمیکند. با این حال، وقتی از islice میخواهیم تا پایانش برود، پنج عضوِ نخستِ قاب مصرف میشوند. خالی بودنِ خروجی، شاهدِ دستنخوردهماندنِ ورودی نیست. در مقابل، ساختنِ همان برش و تعیینِ حدِ درخواستِ صفر، متدِ دریافتِ مقدارِ منبعِ کنترلشدهٔ ما را صدا نمیزند. تکرارگر بودنِ یک شیء را باید از درخواستِ واقعیِ عضو جدا کرد.
برای توقفِ متناهی و تخلیهٔ کامل، مرزِ موفقِ منبع برابرِ کوچکترِ طولِ داده و بیشینهٔ شروع و توقف است. گام روی این مرز اثر ندارد؛ تعداد و جایگاهِ خروجیها را عوض میکند. اگر منبع پیش از رسیدن به آن مرز تمام شود، عضوِ تازهای برای عبور از انتها ساخته نمیشود. اگر توقف None باشد، فقط منبعِ متناهیِ تعریفشدهٔ ما تا پایان پیموده میشود؛ همین تنظیم را روی ورودیِ بیپایان آزمایش نکردهایم.
تلاشِ پایان، برداشتِ موفق نیست. برای دادهٔ هفتعضوی، توقفِ هفت میتواند بدونِ درخواستِ عضوِ هشتمِ منبع خاتمه یابد؛ توقفِ هشت باید نبودِ آن عضو را مشاهده کند. هر دو هفت برداشتِ موفق دارند، ولی شمارِ تلاشهایِ منبع فرق میکند. این شمارِ دقیق و رفتارِ درخواستِ دوباره از برشِ پایانیافته، مشاهدهٔ نسخهٔ CPython و منبعِ تألیفیِ همین فایل است؛ وعدهای عمومی دربارهٔ تمامِ پیادهسازیها نیست.
بخشِ کاملِ islice در منبعِ ثابتِ CPython، ترتیبِ ردکردن، کنترلِ توقف و گرفتنِ مقدار را روشن میکند. آن بخش خوانده شده، اما مفسر را از آن دوباره نساختهایم. مستندِ API و فایلِ پیادهسازی هر دو متعلق به پروژهٔ پایتوناند؛ دو اعتبارسنجیِ تجربیِ مستقل محسوب نمیشوند. مرجعِ مستقلِ عددیِ درس را جدا میسازیم.
۴. کدِ محدود و مرجعِ حسابیِ مستقل

ورودی دقیقاً تاپلِ حداکثر دوازده شمارشِ صحیحِ صفر تا ۴۰۹۵ است. شروع و توقفِ متناهی بینِ صفر و دوازده، گام بینِ یک و چهار و حدِ درخواست بینِ صفر و دوازده قرار دارند. None برای توقف یا حدِ درخواست معنایِ جدا دارد. bool، زیرکلاس، اعشار، تبدیلِ ضمنی و مولدِ دلخواه رد میشوند. همهٔ اعتبارسنجیها پیش از ساختنِ منبع انجام میشوند؛ سقفها انتخابِ آموزشیِ ما هستند.
"""Actual islice on a bounded synthetic ADC frame; no external source or I/O."""
from itertools import islice, product
import json
import platform
import sys
MAX_ROWS = 12
MAX_INDEX = 12
MAX_STEP = 4
MAX_COUNT = 4095
def _integer(value, name, low, high):
if type(value) is not int:
raise TypeError(name + ' must be an exact int, excluding bool')
if not low <= value <= high:
raise ValueError(name + ' is outside the own bounded contract')
def _admit(rows, start, stop, step, limit):
if type(rows) is not tuple:
raise TypeError('rows must be an exact materialized tuple')
if len(rows) > MAX_ROWS:
raise ValueError('own frame cap is 12 rows')
for value in rows:
_integer(value, 'synthetic ADC count', 0, MAX_COUNT)
_integer(start, 'start', 0, MAX_INDEX)
if stop is not None:
_integer(stop, 'stop', 0, MAX_INDEX)
_integer(step, 'step', 1, MAX_STEP)
if limit is not None:
_integer(limit, 'output request limit', 0, MAX_ROWS)
class _ObservedSource:
"""Own finite sticky source; attempts and successful pulls are separate."""
def __init__(self, rows):
self.rows = rows
self.position = 0
self.attempts = 0
self.pulled = []
def __iter__(self):
return self
def __next__(self):
self.attempts += 1
if self.position == len(self.rows):
raise StopIteration
item = (self.position, self.rows[self.position])
self.position += 1
self.pulled.append(item[0])
return item
def trace_slice(rows_counts, start, stop, step, limit=None):
"""Snapshot at full islice termination or after at most limit outputs."""
_admit(rows_counts, start, stop, step, limit)
source = _ObservedSource(rows_counts)
selected = islice(source, start, stop, step)
construction_attempts = source.attempts
results = []
stop_seen = False
requests = 0
while limit is None or requests < limit:
requests += 1
try:
results.append(next(selected))
except StopIteration:
stop_seen = True
break
# Direct indexing of immutable input does not ask the source for its tail.
return {'selected_indices': tuple(x[0] for x in results),
'selected_counts': tuple(x[1] for x in results),
'source_successes': source.position,
'source_next_attempts_observed': source.attempts,
'source_pulled_indices': tuple(source.pulled),
'unconsumed_indices': tuple(range(source.position, len(rows_counts))),
'unconsumed_counts': rows_counts[source.position:],
'islice_stop_observed': stop_seen,
'construction_source_attempts': construction_attempts}
def arithmetic_reference(rows, start, stop, step, limit):
"""Valid finite fixtures only; index predicates, no iterator or main calls."""
n = len(rows)
available = tuple(i for i in range(n)
if i >= start and (stop is None or i < stop)
and (i-start) % step == 0)
positions = available if limit is None else available[:limit]
stopped = limit is None or limit > len(available)
if stopped:
bound = n if stop is None else max(start, stop)
frontier = min(n, bound)
attempts = frontier + int(stop is None or n < bound)
else:
frontier = positions[-1] + 1 if positions else 0
attempts = frontier
return {'selected_indices': positions,
'selected_counts': tuple(rows[i] for i in positions),
'source_successes': frontier, 'source_next_attempts_observed': attempts,
'source_pulled_indices': tuple(range(frontier)),
'unconsumed_indices': tuple(range(frontier, n)),
'unconsumed_counts': rows[frontier:],
'islice_stop_observed': stopped, 'construction_source_attempts': 0}
def _raises(exception, function, *args):
try:
function(*args)
except exception:
return True
return False
def run_example():
checks = {}
def check(name, value):
assert type(value) is bool and value, name
checks[name] = value
rows = (101, 103, 107, 109, 113, 127, 131, 137)
full = trace_slice(rows, 1, 7, 3)
partial = trace_slice(rows, 1, 7, 3, 2)
check('literal_stride_outputs_two_but_full_slice_consumes_seven',
full['selected_indices'] == (1, 4) and full['selected_counts'] == (103, 113)
and full['source_pulled_indices'] == (0, 1, 2, 3, 4, 5, 6)
and full['source_successes'] == full['source_next_attempts_observed'] == 7
and full['unconsumed_counts'] == (137,) and full['islice_stop_observed'])
check('same_two_outputs_finite_limit_stops_before_terminal_tail_skip',
partial['selected_counts'] == full['selected_counts']
and partial['source_successes'] == partial['source_next_attempts_observed'] == 5
and partial['unconsumed_counts'] == (127, 131, 137)
and partial['islice_stop_observed'] is False)
empty_range = trace_slice(rows, 5, 2, 1)
check('empty_reversed_range_still_advances_to_start_when_drained',
empty_range['selected_indices'] == () and empty_range['source_successes'] == 5
and empty_range['unconsumed_counts'] == (127, 131, 137)
and empty_range['islice_stop_observed'])
no_requests = trace_slice(rows, 5, 2, 1, 0)
check('construction_and_zero_output_limit_make_no_source_next_attempts',
no_requests['construction_source_attempts'] == no_requests['source_successes']
== no_requests['source_next_attempts_observed'] == 0
and no_requests['unconsumed_counts'] == rows and not no_requests['islice_stop_observed'])
finite_boundary = trace_slice(rows[:7], 0, 7, 4)
actual_end = trace_slice(rows[:7], 0, 8, 4)
source = _ObservedSource(rows[:2])
exhausted_slice = islice(source, 0, None, 1)
assert tuple(exhausted_slice) == ((0, 101), (1, 103))
before_repeated_stop = source.attempts
sticky = all(_raises(StopIteration, next, exhausted_slice) for unused in range(3))
check('finite_boundary_vs_source_exhaustion_attempts_and_spent_slice_observation',
finite_boundary['selected_counts'] == actual_end['selected_counts'] == (101, 113)
and finite_boundary['source_successes'] == actual_end['source_successes'] == 7
and finite_boundary['source_next_attempts_observed'] == 7
and actual_end['source_next_attempts_observed'] == 8
and before_repeated_stop == source.attempts == 3 and sticky)
no_stop = trace_slice(rows, 2, None, 3)
check('stop_None_drains_only_the_declared_finite_source',
no_stop['selected_indices'] == (2, 5) and no_stop['selected_counts'] == (107, 127)
and no_stop['source_successes'] == 8 and no_stop['source_next_attempts_observed'] == 9
and no_stop['unconsumed_counts'] == () and no_stop['islice_stop_observed'])
cases = 0
for n, start, stop, step, limit in product(range(9), range(11),
tuple(range(11))+(None,), range(1, 5), (None, 0, 1, 3)):
fixture = tuple(100 + 3*i for i in range(n))
actual = trace_slice(fixture, start, stop, step, limit)
expected = arithmetic_reference(fixture, start, stop, step, limit)
assert actual == expected, (n, start, stop, step, limit, actual, expected)
cases += 1
check('19008_finite_profiles_match_arithmetic_index_reference_ONE_group', cases == 19008)
by_step = [trace_slice(rows, 1, 7, step) for step in range(1, 5)]
check('step_changes_selected_indices_not_fully_consumed_finite_frontier',
tuple(len(x['selected_indices']) for x in by_step) == (6, 3, 2, 2)
and all(x['source_successes'] == 7 and x['unconsumed_counts'] == (137,) for x in by_step))
duplicates = trace_slice((0, 0, 5, 0, 0), 0, 5, 2)
check('equal_values_are_distinct_source_occurrences_not_deduplicated',
duplicates['selected_indices'] == (0, 2, 4) and duplicates['selected_counts'] == (0, 5, 0)
and duplicates['source_pulled_indices'] == (0, 1, 2, 3, 4))
shared = _ObservedSource(rows)
first = tuple(islice(shared, 0, 3, 2))
after_first = shared.position
second = tuple(islice(shared, 1, 4, 2))
check('successive_slices_use_current_source_origin_not_original_indices',
first == ((0, 101), (2, 107)) and after_first == 3
and second == ((4, 113), (6, 131)) and shared.position == 7
and rows[shared.position:] == (137,))
untouched = _ObservedSource(rows)
ordinary_slice = rows[1:7:3]
check('materialized_tuple_slice_does_not_advance_a_separate_source_cursor',
ordinary_slice == full['selected_counts'] and untouched.position == untouched.attempts == 0
and next(untouched) == (0, 101) and untouched.position == 1)
class IntChild(int):
pass
class TupleChild(tuple):
pass
class IndexTrap:
calls = 0
def __index__(self):
self.calls += 1
return 1
invalid_rows = ([], TupleChild((1,)), tuple(range(13)), (True,), (1.0,),
(float('nan'),), (float('inf'),), ('1',), (IntChild(1),), (-1,), (4096,))
constructed = []
original_source = _ObservedSource
def construction_trap(*args):
constructed.append(args)
raise AssertionError('Invalid input reached source construction')
globals()['_ObservedSource'] = construction_trap
try:
rows_rejected = all(_raises((TypeError, ValueError), trace_slice, bad, 0, 1, 1) for bad in invalid_rows)
finally:
globals()['_ObservedSource'] = original_source
check('strict_materialized_frame_types_values_and_cap_before_source_construction',
rows_rejected and constructed == [])
trap = IndexTrap()
parameter_bad = [(rows, v, 7, 1, None) for v in (True, 1.0, IntChild(1), -1, 13, trap)]
parameter_bad += [(rows, 0, v, 1, None) for v in (False, 1.0, -1, 13, trap)]
parameter_bad += [(rows, 0, 7, v, None) for v in (True, 0, -1, 5, trap)]
parameter_bad += [(rows, 0, 7, 1, v) for v in (True, 1.0, -1, 13, trap)]
consumed = []
def generated():
consumed.append(True)
yield 1
generator = generated()
globals()['_ObservedSource'] = construction_trap
try:
settings_rejected = all(_raises((TypeError, ValueError), trace_slice, *args) for args in parameter_bad)
generator_rejected = _raises(TypeError, trace_slice, generator, 0, 1, 1)
finally:
globals()['_ObservedSource'] = original_source
generator.close()
check('strict_slice_settings_no_implicit_index_and_no_generator_consumption',
settings_rejected and generator_rejected and not consumed and not constructed and trap.calls == 0)
earlier = trace_slice(rows, 1, 7, 3)
detached = trace_slice(rows, 1, 7, 3)
detached['selected_counts'] = (999,)
detached['source_successes'] = 999
maximum = trace_slice((0, 4095)*6, 0, 12, 4)
check('empty_maximum_immutable_inputs_and_fresh_detached_snapshot_dicts',
trace_slice((), 0, 0, 1)['source_next_attempts_observed'] == 0
and maximum['selected_indices'] == (0, 4, 8) and maximum['source_successes'] == 12
and maximum['unconsumed_counts'] == () and earlier == full
and earlier is not detached and rows == (101, 103, 107, 109, 113, 127, 131, 137)
and all(type(x) is tuple for key, x in earlier.items() if key in
('selected_indices', 'selected_counts', 'source_pulled_indices', 'unconsumed_indices', 'unconsumed_counts')))
assert len(checks) == 14 and all(type(x) is bool and x for x in checks.values())
return {'runtime': {'python': platform.python_version(), 'implementation': platform.python_implementation(),
'dependencies': 'standard library only; actual itertools.islice'},
'profile': {'rows': 'exact tuple0..12; exact integer synthetic ADC counts0..4095',
'start': 'exact int0..12', 'stop': 'None or exact int0..12',
'step': 'exact int1..4', 'output_limit': 'None or exact int0..12',
'source': 'own sticky finite iterator; no exceptions except StopIteration',
'units': 'ADC counts synthetic and unitless; positions/attempts count records'},
'main_counts': rows, 'fully_consumed_slice_1_7_3': full,
'same_slice_output_limit_2': partial, 'empty_range_5_2_1': empty_range,
'zero_output_limit': no_requests, 'stop_None_start_2_step_3': no_stop,
'finite_family_profiles_inside_ONE_group': cases,
'invalid_frames_inside_ONE_group': len(invalid_rows),
'invalid_settings_inside_ONE_group': len(parameter_bad),
'meaningful_check_groups': len(checks), 'checks': checks,
'limits': ['No file, network, sensor, external effects, clocks, threads, benchmark or memory measurement.',
'Full drain and finite output budget differ; consumed source values are never rewound.',
'Exact failed-next attempts are CPython3.14.5/own-source observations, not a portable islice promise.',
'Finite arithmetic-reference profiles are one group, not a proof for arbitrary iterators or unbounded inputs.']}
if __name__ == '__main__':
sys.stdout.buffer.write((json.dumps(run_example(), ensure_ascii=True, indent=2, allow_nan=False)+'\n').encode('ascii'))
مرجع، از روی اندیسهایِ تاپل جایگاههایِ مجاز را با شرطِ بازه و باقیماندهٔ تقسیم انتخاب میکند. اگر بودجهٔ درخواست پیش از نیاز به مشاهدهٔ پایان تمام شود، مرز یک واحد پس از آخرین جایگاهِ انتخابشده است؛ با بودجهٔ صفر، صفر میماند. در حالتِ تخلیه، فرمولِ مرزِ متناهی یا طولِ داده به کار میرود. مسیرِ انتظار از islice، منبعِ ردگیر یا تابعِ اصلی استفاده نمیکند.
خانوادهٔ محدود، طولِ صفر تا هشت، شروعِ صفر تا ده، توقفِ صفر تا ده یا None، چهار گام و چهار انتخابِ بودجه را میسنجد: ۱۹۰۰۸ نمایه در یک گروه. چهارده گروهِ نامدار در مجموع داریم؛ شاهدهایِ دستی، تفاوتِ توقف، ورودیِ مردود، مرزهایِ سقف و استقلالِ گزارش نیز در همین شمار هستند. شمارِ نمایهها را به تعدادِ گروههایِ مستقل تبدیل نمیکنیم و این خانواده، اثباتِ همهٔ تکرارگرها نیست.
{
"runtime": {
"python": "3.14.5",
"implementation": "CPython",
"dependencies": "standard library only; actual itertools.islice"
},
"profile": {
"rows": "exact tuple0..12; exact integer synthetic ADC counts0..4095",
"start": "exact int0..12",
"stop": "None or exact int0..12",
"step": "exact int1..4",
"output_limit": "None or exact int0..12",
"source": "own sticky finite iterator; no exceptions except StopIteration",
"units": "ADC counts synthetic and unitless; positions/attempts count records"
},
"main_counts": [
101,
103,
107,
109,
113,
127,
131,
137
],
"fully_consumed_slice_1_7_3": {
"selected_indices": [
1,
4
],
"selected_counts": [
103,
113
],
"source_successes": 7,
"source_next_attempts_observed": 7,
"source_pulled_indices": [
0,
1,
2,
3,
4,
5,
6
],
"unconsumed_indices": [
7
],
"unconsumed_counts": [
137
],
"islice_stop_observed": true,
"construction_source_attempts": 0
},
"same_slice_output_limit_2": {
"selected_indices": [
1,
4
],
"selected_counts": [
103,
113
],
"source_successes": 5,
"source_next_attempts_observed": 5,
"source_pulled_indices": [
0,
1,
2,
3,
4
],
"unconsumed_indices": [
5,
6,
7
],
"unconsumed_counts": [
127,
131,
137
],
"islice_stop_observed": false,
"construction_source_attempts": 0
},
"empty_range_5_2_1": {
"selected_indices": [],
"selected_counts": [],
"source_successes": 5,
"source_next_attempts_observed": 5,
"source_pulled_indices": [
0,
1,
2,
3,
4
],
"unconsumed_indices": [
5,
6,
7
],
"unconsumed_counts": [
127,
131,
137
],
"islice_stop_observed": true,
"construction_source_attempts": 0
},
"zero_output_limit": {
"selected_indices": [],
"selected_counts": [],
"source_successes": 0,
"source_next_attempts_observed": 0,
"source_pulled_indices": [],
"unconsumed_indices": [
0,
1,
2,
3,
4,
5,
6,
7
],
"unconsumed_counts": [
101,
103,
107,
109,
113,
127,
131,
137
],
"islice_stop_observed": false,
"construction_source_attempts": 0
},
"stop_None_start_2_step_3": {
"selected_indices": [
2,
5
],
"selected_counts": [
107,
127
],
"source_successes": 8,
"source_next_attempts_observed": 9,
"source_pulled_indices": [
0,
1,
2,
3,
4,
5,
6,
7
],
"unconsumed_indices": [],
"unconsumed_counts": [],
"islice_stop_observed": true,
"construction_source_attempts": 0
},
"finite_family_profiles_inside_ONE_group": 19008,
"invalid_frames_inside_ONE_group": 11,
"invalid_settings_inside_ONE_group": 21,
"meaningful_check_groups": 14,
"checks": {
"literal_stride_outputs_two_but_full_slice_consumes_seven": true,
"same_two_outputs_finite_limit_stops_before_terminal_tail_skip": true,
"empty_reversed_range_still_advances_to_start_when_drained": true,
"construction_and_zero_output_limit_make_no_source_next_attempts": true,
"finite_boundary_vs_source_exhaustion_attempts_and_spent_slice_observation": true,
"stop_None_drains_only_the_declared_finite_source": true,
"19008_finite_profiles_match_arithmetic_index_reference_ONE_group": true,
"step_changes_selected_indices_not_fully_consumed_finite_frontier": true,
"equal_values_are_distinct_source_occurrences_not_deduplicated": true,
"successive_slices_use_current_source_origin_not_original_indices": true,
"materialized_tuple_slice_does_not_advance_a_separate_source_cursor": true,
"strict_materialized_frame_types_values_and_cap_before_source_construction": true,
"strict_slice_settings_no_implicit_index_and_no_generator_consumption": true,
"empty_maximum_immutable_inputs_and_fresh_detached_snapshot_dicts": true
},
"limits": [
"No file, network, sensor, external effects, clocks, threads, benchmark or memory measurement.",
"Full drain and finite output budget differ; consumed source values are never rewound.",
"Exact failed-next attempts are CPython3.14.5/own-source observations, not a portable islice promise.",
"Finite arithmetic-reference profiles are one group, not a proof for arbitrary iterators or unbounded inputs."
]
}
دو برشِ متوالی رویِ همان منبع، مبدأِ نسبیِ تازه دارند. در کنترلِ کد، برشِ نخست جایگاههایِ اصلیِ صفر و دو و برشِ بعد جایگاههایِ چهار و شش را میگیرد. همچنین مقدارهایِ برابر، رخدادهایِ جدا باقی میمانند. برخلافِ مسئلهٔ عمرِ گروههایِ groupby، اینجا کلید، گروهبندی، مرتبسازی یا نگهداریِ تکرارگرِ زیرگروه نداریم. برشِ معمولِ یک تاپل هم منبعِ جدا را جلو نمیبرد.
۵. چهار پرسش برای ادامهٔ همان منبع

پرسش اول: چرا دو مقدارِ ۱۰۳ و ۱۱۳ میتوانند هفت برداشتِ موفق همراه داشته باشند؟ پاسخ: در تخلیهٔ کامل، اعضایِ پیش از شروع، بینِ انتخابها و انتهایِ فاصله تا توقف نیز از منبع عبور میکنند. فقط جایگاههایِ یک و چهار به خروجی میرسند؛ مرزِ موفقِ منبع هفت است و ۱۳۷ باقی میماند.
پرسش دوم: حدِ درخواستِ دو، همان وضعیتِ پایانِ برش را میسازد؟ پاسخ: خیر؛ پس از دومین خروجی، مرز پنج است و سه مقدار باقی میمانند. درخواستِ پایانیِ اضافی انجام نشده و پرچمِ مشاهدهٔ پایان نادرست است. برابریِ خروجیها، برابریِ دمِ منبع را ثابت نمیکند.
پرسش سوم: خروجیِ تهی یعنی هیچ دادهای خوانده نشده است؟ پاسخ: خیر؛ شروعِ پنج و توقفِ دو، هنگامِ درخواستِ پایان پنج عضو را مصرف میکند. فقط ساختنِ برش یا بودجهٔ درخواستِ صفر، در منبعِ محدود و کنترلشدهٔ ما تلاشِ دریافتِ مقدار ندارد.
پرسش چهارم: این گزارش، رکوردهایِ مصرفشدهٔ فایل یا حسگر را برمیگرداند؟ پاسخ: خیر؛ داده از ابتدا در تاپل موجود است و فقط رفتارِ تکرارگر را مشاهده کردهایم. بازپخش، بازگردانیِ اثرِ بیرونی، مدیریتِ خطاهایِ دلخواه، بستنِ منبع و همزمانی پیاده نشدهاند. اندازهٔ حافظه یا سرعت نیز اندازهگیری نشده است.
منابع در ۳۰ مهر ۱۴۰۵ خوانده شدند؛ مستنداتِ 3.14.9 از منبعِ ثابتِ 3.14.5 و نسخهٔ ثبتشده در خروجی جدا هستند. تاریخِ نخستین انتشارِ صفحهها معلوم فرض نشده است. این آموزش، خبرِ قابلیتِ تازه نیست؛ یک قراردادِ محدود برای خواندنِ دقیقترِ کد و نوشتنِ انتظارِ قابلِ بازتولید است.
منابع و مطالعهٔ بیشتر
بررسی منابع: ۳۰ مهر ۱۴۰۵. تحلیلها و پیشنهادهای اجرایی، جمعبندی نویسندهاند.

