Здравствуйте, Александр,

> Почему нет такой конструкции в большинстве языков программирования? 
> Во-первых, инертность мышления (а что, и так тоже можно?), во-вторых, нужна 
> редко. Обычно ко вложенной безымянной функции относятся не как к функции, а 
> как к блоку кода, который воспринимается как некий фрагмент устойчивой 
> конструкции, функции map, играющей роль цикла. Вложенные безымянные функции 
> обычно небольшие и играют роль части выражения. Если нужно выразить более 
> сложную логику, то её оформляют как отдельную функцию, которой уже логично 
> дать соответствующее имя (независимо от наличия рекурсии внутри). Вообще, 
> лучший способ прокомментировать блок кода — не писать комментарий перед ним, 
> а вынести его в отдельную функцию и дать ей ясное понятное название.

Кое с чем согласен – с инертностью мышления, например, а в других
отношениях я другого мнения, или, по крайней мере, не разделяю
стремление абсолютизировать утверждения.

Вот, например, в истинности утверждения

    «лучший способ прокомментировать блок кода — не писать комментарий
     перед ним, а вынести его в отдельную функцию и дать ей ясное
     понятное название»

я очень сомневаюсь.  Названия помогают понять смысл только в том
случае, когда этот смысл настолько простой, что его можно передать
буквально одним словом.  Во всех остальных случаях имя функции, даже
самое удачное, никак нельзя считать «лучшим способом комментирования».
Если вообще комментировать, то придется подробнее, другими способами.
Но к этой дилемме вопрос, какое у функции название и есть ли вообще
оно у нее, не очень относится.

Более того, вы пишете, что возможность самоцитирования (и цитирования
охватывающей функции) для безымянных функций «нужна редко».  С этим
тоже не могу согласиться.  В своем утверждении вы исходите из своих
же представлений о роли и использовании функций.  Они, конечно, не
лишены основания, но мне кажутся несколько размытыми.  Что есть
«фрагмент», который не функция в функциональном языке?  Какая именно
логика является «более сложной»?  По какому критерию «логично»
закреплять имя за функцией как часть ее реализации (а не только
интерфейса — ведь когда имя рекурсивной функции цитируется в ее теле
это касается реализации)?  На такие вопросы, полагаю, ответить можно
по-разному, поэтому и выводы будут неоднозначными.

(Впрочем, по поводу утверждения «нужна редко», вспомнил о цикле со
след-условием (do-while или repeat-until).  Он часто используется?
Судя по моей практике, крайне редко.  Тем не менее, он есть в почти
всех императивных языках.  А зачем нужен while в языке C и подобных,
когда он — всего лишь частный случай for, с тяжелыми ограничениями?
Насколько часто нужно написать цикл без ни вводной, ни обновляющей
частей?  По-моему, очень редко.  Но и эта конструкция присутствует
практически везде в императивных языках.)

Вообще, выдвинутые вами соображения насчет именования я считаю
частично справедливыми, но кроме них имеются и другие.

Свои соображения в пользу возможности _не_именования любых функций,
в т.ч. рекурсивных, я разделяю на две группы — принципиальные и
практические.

Принципиально в языке, в котором функции являются значениями —
это не требовать именовать функции, так как нет такого требования
и в отношении других видов значений.  Ведь формирование значения —
числа, строки, булевого или же функции, при помощи подходящего
выражения — это одно дело, а именование ;— другое.  Присваивать имя
значению — возможность для программиста, а выбор именовать или нет
должен быть за ним, а не навязывать ему что делать.

Также принципиальным является отличие функции, которая является
значением, от функции с закрепленным в ее теле ее же именем.  Тело
первой функции можно подставить везде, где требуется ее значение
(для вызова или другой цели) — отсюда, в частности, вытекает удобство
механических манипуляций с программой — а со второй так сделать
невозможно.  Уместно ли обязательно ставить функцию во второй,
ограниченный класс, только потому что она рекурсивная?

В плане практическом, возьмем такую ситуацию.  Часто бывает так, что
вызов рекурсивной функции ее пользователем отличается от самовызова —
например, из-за того, что при первом вызове нужно что-то сделать один
раз: как правило, установить какие-то начальные значения.  Но чаще
всего, вызываемой функции нечем отличить первый вызов от самовызовов,
а даже если это возможно, то невыгодно проверять каждый раз.
Это противоречие устраняется вложением в основную функцию
«вспомогательной», которая на самом деле выполняет работу по существу
и, в частности, самовызывается рекурсивно.  «Основная» же функция
только подготавливает начальные параметры к ее работе и, возможно,
как-то изменяет результат до того как передать его пользователю.
(Второй данный мной раньше пример (с обращением списка) — именно
такой случай.)  Но тогда получается так, что вложенную функцию мы
именуем только для того, чтобы она могла себя вызывать; имя ее
пользователю основной функции не нужно.  А может быть и так, что
основная и вложенная функции вызываются взаимнорекурсивно.  В любом
случае нет причины _требовать_ именовать вложенную функцию, ее имя
несущественно — пусть программист сам решит, стоит ли.
Если, вдобавок, вложенная вызывает внешнюю не через фиксированное
имя, а именно как свою охватывающую, то удаляется ненужная связанность
реализаций обеих функций через имена: имя каждой можно изменять, не
трогая реализацию другой; а вложенную функцию можно даже скопировать
в другое подходящее окружение, не изменяя ничего в ней, в частности —
интерфейс к «родителю».

Я не случайно указал на аналогию с местоимениями в естественных
языках – ими тоже пользуются не из каприза, а потому что так разумнее.
Похожие взаимосвязи возникают и в программах, и мне кажется полезным
не пренебрегать этим фактом, а воспользоваться для улучшения их
структуры.

Что касается блоков в Smalltalk-е (и перенятых из него в Ruby):
как подобия безымянных функций они безусловно полезны, но не есть
полноценные функции, так как не являются замыканиями, и даже
значениями являются только в ограниченном смысле.  Конечно, эта
ограниченность в ряде случаев и не мешает.

Ответить