Потому что от того, была выполнена эта оптимизация или нет, зависит семантика неконстантных методов объекта-функции. В исходном джаваскрипте TCO не было. Сейчас она допускается и для этого, как минимум, метод arguments у объектов-функций объявлен deprecated.
2017-08-07 13:04 GMT+03:00 Andrei Klimov <[email protected]>: > Михаил, > > А я не понял: почему обсуждаемые конструкции мешают Tail Call Optimization? > По идее, если вызов действительно хвостовой (поверх него ничего нет), то что > мешает это заметить и преобразовать в цикл? > > Андрей > > 7 авг. 2017 г. 11:20 пользователь "Mike Potanin" <[email protected]> > написал: > >> На мой взглад доступ с фреймам вызова функции через специальный объект >> в джаваскрипте - решение неудачное, так как оно не позволяет >> производить TCO. В функциональном языке какие-то варианты TCO очень >> важны, так как руками рекурсию в цикл превратить не получится. >> >> 2017-08-04 23:56 GMT+03:00 Александр Коновалов <[email protected]>: >> > Добрый вечер, Бойко! >> > >> > Понял вашу мысль. Вы фактически предлагаете аналог указателя «this» (или >> > «self») применительно к безымянным функциям. Идея интересная, но у меня >> > затык не с синтаксической конструкцией, а с представлением во время >> > выполнения. Хоть с именами, хоть с this’ом взаимная рекурсия с возвратом >> > указателя неизбежно конфликтует (а) с требованиями к равенству двух >> > указателей на функцию, (б) с использованием простого счётчика ссылок для >> > управления памятью. >> > >> > Почему нет такой конструкции в большинстве языков программирования? >> > Во-первых, инертность мышления (а что, и так тоже можно?), во-вторых, нужна >> > редко. Обычно ко вложенной безымянной функции относятся не как к функции, а >> > как к блоку кода, который воспринимается как некий фрагмент устойчивой >> > конструкции, функции map, играющей роль цикла. Вложенные безымянные функции >> > обычно небольшие и играют роль части выражения. Если нужно выразить более >> > сложную логику, то её оформляют как отдельную функцию, которой уже логично >> > дать соответствующее имя (независимо от наличия рекурсии внутри). Вообще, >> > лучший способ прокомментировать блок кода — не писать комментарий перед >> > ним, >> > а вынести его в отдельную функцию и дать ей ясное понятное название. >> > >> > >> > Не могу не поделиться одной историей. В этом году я разрешил студентам >> > сдавать лабораторные на необычных языка программирования, при этом одна >> > лабораторная засчитывалась за три. Среди таких языков был Smalltalk. Порог >> > вхождения у него низкий: посмотрев на примеры кода и найдя документацию по >> > стандартной библиотеке, уже можно писать немаленькие (сотни строк кода) >> > работающие программы. При этом вникать в устройство языка не обязательно. >> > >> > Так вот. «Условный оператор» в Smalltalk выглядит так: (x < y) ifTrue: >> > […] ifFalse: […]., «цикл» — [x < y] whileTrue: […].. Так вот. Это не >> > условный оператор и не цикл. Текст в квадратных скобках — это не блок кода >> > (как {…} в C++), а лямбда. Безымянная функция (вернее, безымянный класс с >> > методом call, если я ничего не путаю). Булевский класс имеет двух потомков, >> > соответственно, True и False. У первого метод ifTrue: f1 ifFalse f2. >> > выполняет замыкание f1, у второго — f2. У самого класса замыкания, помимо >> > метода call, есть метод whileTrue: func., который вычисляет себя и свой >> > аргумент в цикле, пока сам себе возвращает значение True. >> > >> > А значит, вместо (x < y) ifTrue: […] ifFalse: [……]. можно написать: f1 >> > := […]. f2 := [……]. (x < y) ifTrue: f1 ifFalse f2. Аналогично с «циклом» >> > whileTrue: f3 := [x < y]. f4 := […]. f3 whileTrue: f4.. >> > >> > Когда я это объяснял студентам (и делал замены из предыдущего абзаца), >> > только что написавшим (и правильно написавшим) лабораторную, они >> > удивлялись. >> > Оказывалось, что в языке нет управляющих конструкций кроме создания блока, >> > вызова метода и ^ (так обозначается возврат из метода). Ветвление и цикл — >> > методы библиотечных классов. >> > >> > Это я рассказал к тому, что безымянные функции вкупе с функциями >> > стандартной библиотеки (map, fold, filter и другие) воспринимаются как >> > цельные синтаксические конструкции, фактически, как встроенные операторы >> > языка. Помимо Smalltalk’а (а он позиционируется как >> > объектно-ориентированный, а не функциональный), могучая синтаксическая >> > поддержка таких конструкций есть в Ruby и Kotlin’е (не программировал, могу >> > что-то путать ошибаться). В последнем, например, если функция/метод >> > принимает последним аргументом функцию, то её можно записывать как просто >> > блок в фигурных скобках _вне_ круглых скобок: вместо foo(x, y, {…}) можно >> > писать foo(x, y) {…}. Если аргумент единственный — скобки не нужны: >> > foo({…}) >> > эквивалентно foo {…}. Что и позволяет писать конструкции, выглядящие почти >> > как нативные синтаксические конструкции (типа if(…) {…}). >> > >> > -----Original Message----- >> > From: Boyko Bantchev [mailto:[email protected]] >> > Sent: Friday, August 4, 2017 10:28 AM >> > To: [email protected] >> > Subject: Re: FW: Рефал умер? >> > >> > Здравствуйте, Александр! >> > >> >> Вы не могли бы пояснить, что вы имеете ввиду под «цитированием себя и >> >> охватывающей функции при помощи … стандартной пары знаков или имён»? >> > >> > Имею ввиду возможность, заложенную в языке, любой функции вызывать себя >> > через какое-то фиксированное, универсальное для этой роли имя. >> > Ну, или передавать свой адрес кому-то еще. Подобно местоимениям «я» или >> > «себя»/«себе» в человеческой речи — человек говорит о себе, не нуждаясь в >> > имени. Местоимение одно и то же, но относится к разным людям в зависимости >> > от того, кто говорит. Конкретный синтаксис неважен. >> > >> > Один из совсем немногих языков, где это имеется — это JavaScript. >> > Дам пример на нем. >> > >> > В этом языке у каждой функций имеется (составная) переменная arguments, >> > а у arguments имеется часть callee. Так вот, arguments.callee — это то, о >> > чем я говорю. В результате можно написать, скажем, анонимный рекурсивный >> > факториал: >> > >> > function(n) {return n<2 ? 1 : n*arguments.callee(n-1)} >> > >> > и вызвать его можно так: >> > >> > (function(n) {return n<2 ? 1 : n*arguments.callee(n-1)})(5) >> > >> > (или передать кому-нибудь как значение, а вызывать будет уже он сам). >> > >> > Еще пример: обращение связанного списка функцией reverse. >> > reverse создает вложенную функцию с двумя аргументами (из первого она >> > накапливает результат во второй) и тут же ее вызывает. Вложенная функция >> > рекурсивна и анонимна. >> > >> > function reverse(x) { >> > return (function(x,y) { >> > if (x) { >> > var r = x >> > x = x.next >> > r.next = y >> > return arguments.callee(x,r) >> > } >> > return y >> > })(x,null) >> > } >> > >> > (А список может быть, скажем, такой: >> > >> > list = {data:1} >> > list = {data:2, next:list} >> > list = {data:3, next:list} >> > ) >> > >> > Иногда полезно подобным стандартным анонимным образом вызывать и >> > охватывающую функцию, возможно даже и ту, которая, в свою очередь, ее >> > охватывает — это позволяет уже взаимную анонимную рекурсию, но таких >> > средств >> > ни в JavaScript, ни в других языках не видел. (Сам я такое свойство вложил >> > в своем полуреализованном языке, но миру от этого мало пользы :) ) >> > >> > Бойко
