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

Я написал следующую функцию как часть разрабатываемого плагина jQuery:

$.fn.append2 = function(collection, callback) {
    var $this = this;

    $.each(collection, function(key, value) {
        $this.append(callback ? callback(key, value) : value);
    });

    return this;
};

После тестирования высокорекурсивной функции, которая сильно зависит от .append2Я оптимизировал .append2 чтобы:

$.fn.append2 = function(collection, callback) {
    var $this = this;

    $.each(collection, callback
        ? function(key, value) { $this.append(callback(key,value)); }
        : function(key, value) { $this.append(value); }
    );

    return this;
};

Хотя этот код более эффективен с точки зрения скорости, он все же оставил меня неудовлетворенным. Я, по сути, определил одну и ту же функцию дважды:

function(key, value) { $this.append(callback(key,value)); }
function(key, value) { $this.append(value); }

И мне было интересно, есть ли какой-нибудь язык, который позволяет мне определять функцию только один раз как:

function(key, value) { $this.append(value); }

А затем оперируйте этим, заменив аргумент на $this.append от value в callback(key, value), (И нет, не манипулируя строками. Управляя самой функцией.)

Есть ли такой язык программирования?

6 ответов

Решение

Любой гомоиконический язык позволяет вам делать это в той или иной степени. Все языки Lisp позволяют вам процедурно строить функции из кусочков и затем вызывать их как обычно. Язык Io является редким в том смысле, что я верю, что он позволит вам пойти другим путем: предоставив функцию, вы можете отделить ее исходный код и манипулировать им.

Хм... ответ вроде да и вроде нет. Если вы хотите изменить саму функцию, то я не верю, что это возможно, потому что функция - это просто двоичные данные. Но вы можете захотеть взглянуть на Scheme: все в Scheme является "списком" (включая тело функции), и, пока вы не оценили тело, вы можете изменить его по своему усмотрению. Взгляните на Eval для получения дополнительной информации.

Многие языки отражают уровень, на котором они допускают замену аргументов и тому подобное (в том числе, Java, C#, Smalltalk и Ruby). Тем не менее, существуют проблемы разделения проблем, когда одна часть программы заменяет другую. Вы можете поставить значение по умолчанию для callback это просто возвращает value; это было бы более элегантное решение, которое удалило бы условное выражение.

Что касается вашего желания "подключить функцию между вызывающим и вызываемым":

взглянем на совет, скажем, в (Emacs) Lisp, и на концепцию аспектно-ориентированного программирования.

Суть дела заключается в следующем выражении:

callback
    ? function(key, value) { $this.append(callback(key,value)); }
    : function(key, value) { $this.append(value); }

В Mathematica вы можете (примерно) переформулировать это следующим образом:

Function[{key, value}, Append[this, callback[key, value]]] /.
  HoldPattern[callback[_, v_]] /; callback === undefined :> v

... учитывая, что this а также undefined не имеют особого значения в Mathematica. Не вдаваясь в подробности, это выражение сначала определяет функцию, которая безоговорочно вызывает callback, Затем он преобразует тело этой функции, чтобы просто использовать value если только если callback имеет значение undefined, В идиоматическом Mathematica есть более простые способы обработки такого простого случая, но он иллюстрирует запрошенную возможность и является полезным инструментом в более сложных ситуациях.

Если я понимаю, что вы ищете, возможно, функторы предоставят решение. Управляя состоянием / параметрами функтора, вы можете достичь описанного вами поведения, изменив объект / информацию, над которой работает функция.