Crazy python
Jul. 20th, 2022 07:16 pmФункция выглядит как вычислимая примитивно-рекурсивная, но на самом деле невычислимая общерекурсивная!
Доказательство
def foo(i):
if i < 0 or i > 10:
return i
try:
print(f"{i} - 1")
return foo(i-1)
finally:
print(f"{i} + 1")
return foo(i+1)
print(foo(5))
На самом деле эта функция - чистое UB в питоне. После долгих лет рассказа о том, что в питоне нет UB.
Вот более интересный генератор последовательностей на той же базе.
def foo(i):
if i < 0 or i > 10:
return i
print(i, end='', sep='')
try:
return foo(i-1)
finally:
return foo(i+1)
print(foo(5))
Чем не генератор случайных чисел?
no subject
Date: 2022-07-20 04:52 pm (UTC)What's ub?
no subject
Date: 2022-07-20 04:54 pm (UTC)undefined behavior. Вещь, которая преследует низкоуровневые языки типа С, С++, Rust (Swift и т.д.).
no subject
Date: 2022-07-20 05:22 pm (UTC)Чё это UB? Language Reference § 8.4 The
trystatement:Банальная нетерминированная взаимная рекурсия между foo(1) и foo(0), с коротким заходом в foo(-1) на каждом круге.
no subject
Date: 2022-07-20 05:32 pm (UTC)Но при этом, если я его поменяю на
То последовательность полностью меняется, превращаясь в 54321212121212121212121212121212121212121212121212...
Более того, если сделать вот так:
То она меняется опять! Хотя, формально, codeflow никак не поменян.
no subject
Date: 2022-07-20 05:38 pm (UTC)Я, кажется, начал понимать. Дело в эксепшенах с рекурсией.
Они съедаются и продолжается выполнение. Вероятнее всего в этот момент всякие нюансы с глубиной стека начинают определять random seed.
no subject
Date: 2022-07-20 05:42 pm (UTC)Ну кек бы условие «базы рекурсии» поменялось — было
< 0, стало< 1. Теперь такая же нетерминированная рекурсия между foo(2) и foo(1) с заходом в foo(0) (которого не видно, потому что он возвращает 0 без print’а).no subject
Date: 2022-07-20 05:46 pm (UTC)Так, а вот продолжать рекурсию при получении
KeyboardInterrupt— это подстава.no subject
Date: 2022-07-20 08:12 pm (UTC)Нет, дело не в лимитах. try/finally прячет except Exception: pass. Что, в свою очередь, означает, что вывод зависит от глубины стека на котором случился exception, что определяет последовательность -1 и +1.
Т.е. у нас действительно нет детерминизма, он он происходит из-за разной глубины стека (внешнего side cause). Хотя всё равно выглядит странно.
no subject
Date: 2022-07-20 08:13 pm (UTC)Ну вот так вот неявный except работает.
Если хочешь убить - Ctrl-Z; kill %%.
no subject
Date: 2022-07-21 07:27 am (UTC)Это для тех, у кого нет соседней вкладки с htop’ом :P