Дело в том, что на самом деле не так много возможностей для кодирования функций. Вот основные варианты:
Переписывание термина: вы сохраняете функции как их абстрактные синтаксические деревья (или некоторую их кодировку. Когда вы вызываете функцию, вы вручную пересекаете синтаксическое дерево, чтобы заменить его параметры аргументом. Это просто, но ужасно неэффективно с точки зрения времени и пространства ,
Замыкания: у вас есть некоторый способ представления функции, возможно, синтаксическое дерево, более вероятно, машинный код. И в этих функциях вы ссылаетесь на свои аргументы по ссылке в некотором роде. Это может быть указатель-смещение, это может быть целое число или индекс Де Брюина, это может быть имя. Затем вы представляете функцию как замыкание : функция «инструкции» (дерево, код и т. Д.) Соединяется со структурой данных, содержащей все свободные переменные функции. Когда функция фактически применяется, она каким-то образом знает, как искать свободные переменные в своей структуре данных, используя среды, арифметику указателей и т. Д.
Я уверен, что есть другие варианты, но это основные, и я подозреваю, что почти каждый другой вариант будет вариантом или оптимизацией базовой структуры замыкания.
Таким образом, с точки зрения производительности замыкания почти всегда работают лучше, чем переписывание терминов. Из вариаций какой лучше? Это сильно зависит от вашего языка и архитектуры, но я подозреваю, что «машинный код со структурой, содержащей свободные переменные» является наиболее эффективным. В нем есть все, что нужно функции (инструкции и значения), и ничего более, и вызов не приводит к большому сроку обхода.
Я заинтересован в использовании обоих популярных алгоритмов кодирования популярных функциональных языков (Haskell, ML)
Я не эксперт, но на 99% большинство ML-версий используют некоторые варианты замыканий, которые я описываю, хотя и с некоторой вероятностью оптимизации. Смотрите это для (возможно, устаревшей) перспективы.
Haskell делает что-то более сложное из-за ленивых вычислений: он использует переписывание графа без тегов .
а также в самом эффективном, который может быть достигнут.
Что наиболее эффективно? Не существует реализации, которая была бы наиболее эффективной для всех входов, поэтому вы получаете реализации, которые в среднем эффективны, но каждая из них будет лучше в разных сценариях. Так что нет определенного рейтинга наиболее или наименее эффективных.
Здесь нет магии. Чтобы сохранить функцию, вам нужно как-то хранить ее свободные значения, иначе вы кодируете меньше информации, чем имеет сама функция. Может быть, вы можете оптимизировать некоторые свободные значения с помощью частичной оценки, но это рискованно для производительности, и вы должны быть осторожны, чтобы гарантировать, что это всегда останавливается.
И, может быть, вы можете использовать какой-то тип сжатия или умный алгоритм для повышения эффективности использования пространства. Но тогда вы либо тратите время на пространство, либо попадаете в ситуацию, когда вы оптимизировали для одних случаев и замедлили для других.
Вы можете оптимизировать для общего случая, но то , что общий случай является может измениться на язык, область применения и т.д. тип кода , который быстро для видеоигры (номер хруст, узкие петли с большим входом), вероятно , отличается от что быстро для компилятора (обходы дерева, рабочие списки и т. д.).
Бонус: существует ли такое кодирование, которое отображает целые числа, закодированные в функции, на собственные целые числа (short, int и т. Д. В C). Это вообще возможно?
Нет, это невозможно. Проблема в том, что лямбда-исчисление не позволяет вам анализировать термины. Когда функция принимает аргумент того же типа, что и церковная цифра, она должна иметь возможность вызывать его, не изучая точное определение этой цифры. Это то же самое, что и с церковными кодировками: единственное, что вы можете с ними сделать, это позвонить им, и вы можете имитировать все полезное с этим, но не без затрат.
Что еще более важно, целые числа занимают все возможные двоичные кодировки. Поэтому, если бы лямбды были представлены как их целые числа, у вас не было бы возможности представлять нецерковно-числовые лямбды! Или, вы бы ввели флаг, чтобы обозначить, является ли лямбда цифрой или нет, но тогда любая эффективность, которую вы хотите, вероятно, исчезнет из окна.
РЕДАКТИРОВАТЬ: С момента написания этого, мне стало известно о третьем варианте для реализации функций более высокого порядка: дефункциональность . Здесь каждый вызов функции превращается в большой switch
оператор, в зависимости от того, какая лямбда-абстракция была задана как функция. Компромисс здесь заключается в том, что это целое преобразование программы: вы не можете скомпилировать части по отдельности, а затем связать их вместе таким образом, поскольку вам нужно заранее иметь полный набор лямбда-абстракций.