Оптимізація комбінаторів та хвостових викликів Y


20

Визначення комбінатора Y у F # є

let rec y f x = f (y f) x

f розраховує мати в якості першого аргументу деяке продовження для рекурсивних підпроблем. Використовуючи yf як продовження, ми бачимо, що f буде застосовуватися до послідовних викликів у міру розвитку

let y f x = f (y f) x = f (f (y f)) x = f (f (f (y f))) x etc...

Проблема полягає в тому, що, апріорі, ця схема виключає використання будь-якої оптимізації хвостових викликів: дійсно, у f може бути якась операція, і в цьому випадку ми не можемо просто мутувати локальний кадр стека, пов'язаний з f.

Так :

  • з одного боку, використання комбінатора Y вимагає явного іншого продовження, ніж сама функція.
  • якщо інше застосувати TCO, ми хотіли б не мати операції, що очікує на f, і лише викликати f.

Чи знаєте ви про якийсь спосіб, щоб ці двоє могли примиритися? Як Y з акумуляторним трюком, або Y з фокусом CPS? Або аргумент, що доводить, що це неможливо зробити?


Ви додали повторну ключову роботу до своєї реалізації? Я повинен подумати, що це потрібно з мого читання ..
Джиммі Хоффа

Чи є у вас докази, що це не оптимізує хвостовий дзвінок? Я думаю, ви можете прочитати ІЛ для цієї функції і побачити, я не здивувався б, якщо компілятор досить розумний, щоб щось придумати ..
Джиммі Хоффа

у випадку прямої непрямої рекурсії це не відбувається: проте ви можете переписати її, щоб дозволити таке, за умови, що кадр стека повторно використовується через y виклик. так, можливо, доведеться побачити ІЛ, не маючи цього досвіду.
Ніколас

5
Я зареєстрував рахунок і отримав 50 балів, просто коментуючи тут. Це питання справді цікаве. Я думаю, це повністю залежить від цього f. Ми можемо побачити, що це yмогло б задзвонити fз громом (y f), але, як ви кажете, fможе бути деяка очікувана операція. Я думаю, було б цікаво дізнатися, чи є окремий комбінатор, який є більш сприятливим для зворотного дзвінка. Цікаво, чи привернеться це питання до уваги на сайті CS Stackexchange?
Джон Картрайт

Відповіді:


4

Чи знаєте ви про якийсь спосіб, щоб ці двоє могли примиритися?

Ні, і з поважної причини ІМХО.

Y-комбінатор - теоретична конструкція, і він потрібен лише для того, щоб зробити обчислення лямбда повним (пам’ятайте, що в обчисленні лямбда немає циклів, а також у лямбда не є імен, які ми могли б використовувати для рекурсії).

Як такий, комбінатор Y справді захоплюючий.

Але : Ніхто насправді не використовує Y-комбінатор для фактичної рекурсії! (За винятком можливо для задоволення, щоб показати, що це справді працює.)

Оптимізація хвостових викликів, OTOH, є, як видно з назви, оптимізацією. Це нічого не додає виразності мови, ми хвилюємося лише через практичні міркування, такі як простір стеків та виконання рекурсивного коду.

Отже, ваше запитання виглядає так: чи існує апаратна підтримка для зменшення бета-версії? (Бета-скорочення - це зменшення лямбда-виразів, ви знаєте.) Але жодна функціональна мова (наскільки мені відомо) не компілює свій вихідний код для подання лямбда-виразів, які будуть бета-скороченими під час виконання.


2
Y-комбінатор - це як виправлення вузла, який постійно розв’язується після кожного використання. Більшість систем коротко скорочують це і зав'язують вузол на метарівні таким чином, щоб його ніколи не потрібно було усувати.
Ден Д.

1
Щодо останнього абзацу, розглянемо Хаскелл, який в основі використовує скорочення графіків, щоб зробити ледачу оцінку. Але моїм улюбленим є оптимальне зменшення, яке завжди бере шлях у решітці Церква-Розсер з найменшими скороченнями до повної нормальної форми. Таке, як показано в «Оптимальній реалізації мов функціонального програмування» Асперті та Герріні . Також дивіться BOHM 1.1 .
Ден Д.

@DanD. Дякуємо за посилання, я спробую їх пізніше в браузері, що знає постскрипт. Впевнений, що мені чомусь навчитися. Але ви впевнені, що складений haskell зменшує графік? Я сумніваюся в цьому.
Інго

1
Фактично він використовує скорочення графіків: "GHC компілюється до безшпинної беззмістової G-машини (STG). Це умовна машина скорочення графіків (тобто віртуальна машина, яка виконує скорочення графіків, як описано вище)." З ... Докладніше про машину STG дивіться у розділі " Легкі функціональні мови Саймона Пейтона Джонса" на фондовому обладнанні: G-машина безхребетної Tagless .
Ден Д.

@DanD. У тій же статті, яку ви пов’язали, далі йдеться, що GHC "робить ряд оптимізацій щодо цього представлення, перш ніж остаточно скомпілювати його в реальний машинний код (можливо, через C за допомогою GCC)".
Інго

0

Я не зовсім впевнений у цій відповіді, але це найкраще, що я міг придумати.

Комбінатор y по своїй суті лінивий, строгими мовами лінь потрібно додавати вручну через додаткові лямбда.

let rec y f x = f (y f) x

Ваша дефініція виглядає так, що для її припинення потрібна лінь, або (y f)аргумент ніколи не закінчуватиметься, і доведеться оцінювати, fвикористовував він чи ні . ТОК в ледачому контексті є складнішим, і, крім того, результат (y f)повторної композиції функції без застосування x. Я не впевнений, що для цього потрібно взяти O (n) пам’ять, де n - глибина рекурсії, але я сумніваюся, ви могли б досягти того самого типу TOC, як це можливо, з чимось на зразок (перехід на Haskell, тому що я насправді не знаю F #)

length acc []    = acc
length acc (a:b) = length (acc+1) b 

Якщо ви цього ще не знаєте, різниця між foldlі foldl'в Haskell може висвітлити ситуацію. foldlпишеться так, як це було б зроблено нетерплячою мовою. Але замість того, щоб бути TOC'd, це насправді гірше, ніж foldrтому, що акумулятор зберігає потенційно величезний громовідвід, який неможливо частково оцінити. (Це пов’язано з тим, чому і foldl, і foldl 'не працюють у нескінченних списках.) Таким чином, в останніх версіях Haskell foldl'додано, що змушує оцінювати акумулятор кожного разу, коли функція повторюється, щоб забезпечити не створення величезного блоку. Я впевнений, що http://www.haskell.org/haskellwiki/Foldr_Foldl_Foldl%27 може пояснити це краще, ніж я.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.