Что это означает, что язык программирования не имеет стеков?
В соответствии с этим ответом
https://stackoverflow.com/info/551950/what-stackless-programming-languages-are-available/671296#671296
все эти языки программирования являются стекированными
- Stackless Python
- PyPy
- Lisp
- Схема
- Tcl
- Lua
- Parrot VM
Что это значит для них, чтобы быть без штабелирования? Означает ли это, что они не используют стек вызовов? Если они не используют стек вызовов, что они используют?
Ответы
Ответ 1
Что это значит для них, чтобы быть без штабелирования? Означает ли это, что они не используют стек вызовов?
Да, это правильно.
Если они не используют стек вызовов, что они используют?
Точная реализация, конечно, будет отличаться от языка к языку. В Stackless Python есть диспетчер, который запускает интерпретатор Python, используя самый верхний фрейм и его результаты. Интерпретатор обрабатывает коды операций по мере необходимости по одному, пока не достигнет кода операции CALL_FUNCTION
, сигнала, который вы собираетесь ввести в функцию. Это заставляет диспетчера создавать новый кадр с соответствующей информацией и возвращаться к диспетчеру с флагом размотки. Оттуда диспетчер начинает заново, указывая на переводчика в верхнем кадре.
Неупакованные языки избегают стеков вызовов по ряду причин, но во многих случаях они используются так, что некоторые конструкции программирования становятся намного проще реализовать. Канонический - продолжения. Продолжения - это очень мощные, очень простые структуры управления, которые могут представлять любую из обычных структур управления, которые вы, вероятно, уже знакомы (while
, do
, if
, switch
и т.д.).
Если это сбивает с толку, вы можете попробовать обернуть голову вокруг статьи в Википедии и, в частности, скелетной аналогии продолжения продолжения:
Скажи, что ты на кухне перед холодильником, думая о бутерброде. Вы берете продолжение прямо там и вставляете его в карман. Затем вы достаете из холодильника индейку и хлеб и сделайте себе бутерброд, который теперь сидит на прилавке. Вы вызываете продолжение в кармане, и снова оказываетесь перед холодильником, думая о бутерброде. Но, к счастью, на прилавке есть бутерброд, и все материалы, используемые для его изготовления, исчезли. Итак, вы едите его.
Ответ 2
Они не используют стек вызовов, потому что они работают в стиле продолжения прохождения. Если вы не знакомы с оптимизацией хвостового вызова, это, вероятно, хороший первый шаг к пониманию того, что это значит.
Чтобы подражать традиционному вызову/возврату на этой модели, вместо того, чтобы нажимать обратный адрес и ожидая, что оставшаяся часть кадра останется нетронутой, вызывающий абонент замыкается над остальной частью своего кода и любыми необходимыми переменными (остальные освобожден). Затем он выполняет вызов хвоста вызываемому, передавая это продолжение в качестве аргумента. Когда вызываемый "возвращает", он делает это, вызывая это продолжение, передавая возвращаемое значение в качестве аргумента.
Что касается вышеизложенного, это просто сложный способ выполнения вызовов функций. Однако он очень хорошо обобщает более сложные сценарии:
- exception/finally/etc блоки очень легко смоделированы - если вы можете передать одно "возвращение" в качестве аргумента, вы можете передать 2 (или более) так же легко. lisp -y "обработчик условий" (которые могут или не могут возвращать управление вызывающему) также легко - передайте продолжение для остальной части этой функции, которая может быть или не быть вызвана.
- Множественные возвращаемые значения аналогичным образом упрощаются - передайте несколько аргументов продолжению.
- Возвращаемые временные файлы/копирование больше не отличаются от передачи аргументов функции. Это часто упрощает устранение временных рядов.
- Оптимизация регенерации хвоста тривиальна: вызывающий объект просто передает полученное им возвращение, а не захватывает новый.