Подтвердить что ты не робот

Какие языки поддерживают * рекурсивные * функции литералов/анонимных функций?

Кажется, что в настоящее время несколько основных языков поддерживают литералы функций. Их также называют анонимными функциями, но мне все равно, есть ли у них имя. Важно то, что литерал функции является выражением, которое дает функцию, которая еще не определена в другом месте, поэтому, например, в C, &printf не учитывается.

EDIT, чтобы добавить: если у вас есть истинное выражение функции <exp>, вы должны передать его функции f(<exp>) или сразу применить ее к аргументу, т.е. <exp>(5).

Мне любопытно, какие языки позволяют писать литералы функций, которые являются рекурсивными. Wikipedia " анонимная рекурсия" статья не дает никаких примеров программирования.

В качестве примера можно использовать рекурсивную факториальную функцию.

Вот те, которые я знаю:

  • JavaScript/ECMAScript может сделать это с помощью callee:

    function(n){if (n<2) {return 1;} else {return n * arguments.callee(n-1);}}
    
  • легко в языках с letrec, например Haskell (который вызывает его let):

    let fac x = if x<2 then 1 else fac (x-1) * x in fac

    и существуют эквиваленты в Lisp и схеме. Обратите внимание, что привязка fac является локальной для выражения, поэтому все выражение на самом деле является анонимной функцией.

Есть ли другие?

4b9b3361

Ответ 1

Большинство языков поддерживают его с помощью Y combinator. Вот пример в Python (из cookbook):

# Define Y combinator...come on Gudio, put it in functools!
Y = lambda g: (lambda f: g(lambda arg: f(f)(arg))) (lambda f: g(lambda arg: f(f)(arg)))

# Define anonymous recursive factorial function
fac = Y(lambda f: lambda n: (1 if n<2 else n*f(n-1)))
assert fac(7) == 5040

Ответ 2

С#

Чтение Wes Dyer's, вы увидите, что ответ @Jon Skeet не совсем корректен. Я не гений на языках, но есть разница между рекурсивной анонимной функцией, и "функция fib действительно просто вызывает делегата, что локальная переменная fib ссылается" на цитату из блога.

Фактический ответ С# будет выглядеть примерно так:

delegate Func<A, R> Recursive<A, R>(Recursive<A, R> r);

static Func<A, R> Y<A, R>(Func<Func<A, R>, Func<A, R>> f)
{
    Recursive<A, R> rec = r => a => f(r(r))(a);
    return rec(rec);
}

static void Main(string[] args)
{
    Func<int,int> fib = Y<int,int>(f => n => n > 1 ? f(n - 1) + f(n - 2) : n);
    Func<int, int> fact = Y<int, int>(f => n => n > 1 ? n * f(n - 1) : 1);
    Console.WriteLine(fib(6));                          // displays 8
    Console.WriteLine(fact(6));
    Console.ReadLine();
} 

Ответ 3

Вы можете сделать это в Perl:

my $factorial = do {
  my $fac;
  $fac = sub {
    my $n = shift;
    if ($n < 2) { 1 } else { $n * $fac->($n-1) }
  };
};

print $factorial->(4);

Блок do не является строго необходимым; Я включил его, чтобы подчеркнуть, что результатом является истинная анонимная функция.

Ответ 4

Ну, кроме общих Lisp (labels) и Scheme (letrec), которые вы уже упоминали, JavaScript также позволяет вам назвать анонимную функцию:

var foo = {"bar": function baz() {return baz() + 1;}};

который может быть более удобным, чем используя callee. (Это отличается от function на верхнем уровне, а последнее приведет к тому, что имя появится в глобальной области тоже, тогда как в первом случае имя появляется только в области самой функции.)

Ответ 5

В Perl 6:

my $f = -> $n { if ($n <= 1) {1} else {$n * &?BLOCK($n - 1)} }
$f(42);  # ==> 1405006117752879898543142606244511569936384000000000

Ответ 6

F # имеет "let rec"

Ответ 7

Вы перепутали какую-то терминологию здесь, функциональные литералы не должны быть анонимными.

В javascript разница зависит от того, написана ли функция как оператор или выражение. Там обсуждается различие в ответах на этот вопрос.

Допустим, вы передаете свой пример функции:

foo(function(n){if (n<2) {return 1;} else {return n * arguments.callee(n-1);}});

Это также можно записать:

foo(function fac(n){if (n<2) {return 1;} else {return n * fac(n-1);}});

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

Анонимная рекурсия снова является чем-то другим, когда функция рекурсирует без ссылки на себя, Y Combinator уже упоминался. В большинстве языков это не обязательно, поскольку доступны лучшие методы. Здесь ссылка на реализация javascript.

Ответ 8

В С# вам нужно объявить переменную для хранения делегата и присвоить ей нуль, чтобы убедиться, что она определенно назначена, затем вы можете вызвать ее из выражения lambda, которое вы ему назначили:

Func<int, int> fac = null;
fac = n => n < 2 ? 1 : n * fac(n-1);
Console.WriteLine(fac(7));

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

Одним из важных вопросов для каждого из этих языков/сред выполнения является поддержка хвостовых вызовов. В С#, насколько мне известно, компилятор MS не использует код операции tail. IL, но JIT может в любом случае оптимизировать его, в определенных обстоятельства. Очевидно, что это очень легко может сделать разницу между рабочей программой и переполнением стека. (Было бы неплохо иметь больше контроля над этим и/или гарантировать, когда это произойдет. В противном случае программа, работающая на одной машине, может сработать с ошибкой другим способом.)

Изменить: как FryHard указал, что это только псевдорекурсия. Достаточно просто, чтобы выполнить работу, но Y-combinator - более чистый подход. Там есть еще одно предостережение от кода, который я написал выше: если вы измените значение fac, все, что пытается использовать старое значение, начнет сбой, потому что выражение лямбда захватило сама переменная fac. (Который он должен, чтобы нормально работать вообще, конечно...)

Ответ 9

Вы можете сделать это в Matlab, используя анонимную функцию, которая использует интроспекцию dbstack(), чтобы получить собственно литерал функции, а затем оценить его. (Я признаю, что это обман, потому что dbstack следует считать экстралингвистическим, но он доступен во всех Matlabs.)

f = @(x) ~x || feval(str2func(getfield(dbstack, 'name')), x-1)

Это анонимная функция, которая отсчитывает от x, а затем возвращает 1. Это не очень полезно, потому что Matlab не имеет оператора?: и запрещает if-блоки внутри анонимных функций, поэтому трудно построить базовый регистр/рекурсивную форму шага.

Вы можете продемонстрировать, что он является рекурсивным, вызывая f (-1); он будет отсчитывать до бесконечности и, в конечном счете, выдает ошибку максимальной рекурсии.

>> f(-1)
??? Maximum recursion limit of 500 reached. Use set(0,'RecursionLimit',N)
to change the limit.  Be aware that exceeding your available stack space can
crash MATLAB and/or your computer.

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

>> feval(@(x) ~x || feval(str2func(getfield(dbstack, 'name')), x-1), -1)
??? Maximum recursion limit of 500 reached. Use set(0,'RecursionLimit',N)
to change the limit.  Be aware that exceeding your available stack space can
crash MATLAB and/or your computer.

Error in ==> [email protected](x)~x||feval(str2func(getfield(dbstack,'name')),x-1)

Чтобы сделать что-то полезное из этого, вы можете создать отдельную функцию, которая реализует рекурсивную логику шага, используя "if", чтобы защитить рекурсивный случай от оценки.

function out = basecase_or_feval(cond, baseval, fcn, args, accumfcn)
%BASECASE_OR_FEVAL Return base case value, or evaluate next step
if cond
    out = baseval;
else
    out = feval(accumfcn, feval(fcn, args{:}));
end

Учитывая, что здесь факториал.

recursive_factorial = @(x) basecase_or_feval(x < 2,...
    1,...
    str2func(getfield(dbstack, 'name')),...
    {x-1},...
    @(z)x*z);

И вы можете назвать это без привязки.

>> feval( @(x) basecase_or_feval(x < 2, 1, str2func(getfield(dbstack, 'name')), {x-1}, @(z)x*z), 5)
ans =
   120

Ответ 10

Также кажется, что Mathematica позволяет вам определять рекурсивные функции, используя #0 для обозначения самой функции, как:

(expression[#0]) &

например. факториал:

fac = Piecewise[{{1, #1 == 0}, {#1 * #0[#1 - 1], True}}] &;

Это соответствует обозначению #i для ссылки на i-й параметр, а соглашение оболочки-скрипта, что script является его собственным 0-м параметром.

Ответ 11

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

(labels ((factorial (x) ;define name and params
    ; body of function addrec
    (if (= x 1)
      (return 1)
      (+ (factorial (- x 1))))) ;should not close out labels
  ;call factorial inside labels function
  (factorial 5)) ;this would return 15 from labels

Ответ 12

Delphi включает анонимные функции с версией 2009.

Пример из http://blogs.codegear.com/davidi/2008/07/23/38915/

type
  // method reference
  TProc = reference to procedure(x: Integer);               

procedure Call(const proc: TProc);
begin
  proc(42);
end;

Использование:

var
  proc: TProc;
begin
  // anonymous method
  proc := procedure(a: Integer)
  begin
    Writeln(a);
  end;               

  Call(proc);
  readln
end.

Ответ 13

Поскольку мне было любопытно, я на самом деле пытался придумать способ сделать это в MATLAB. Это можно сделать, но это выглядит немного Rube-Goldberg-esque:

>> fact = @(val,branchFcns) val*branchFcns{(val <= 1)+1}(val-1,branchFcns);
>> returnOne = @(val,branchFcns) 1;
>> branchFcns = {fact returnOne};
>> fact(4,branchFcns)

ans =

    24

>> fact(5,branchFcns)

ans =

   120

Ответ 14

Анонимные функции существуют в С++ 0x с lambda, и они могут быть рекурсивными, хотя я не уверен в анонимности.

auto kek = [](){kek();}

Ответ 15

'У вас есть идея анонимных функций неправильно, это не просто создание среды выполнения, но и область. Рассмотрим этот макрос схемы:

(define-syntax lambdarec
  (syntax-rules ()
    ((lambdarec (tag . params) . body)
     ((lambda ()
       (define (tag . params) . body)
        tag)))))

Таким образом:

(lambdarec (f n) (if (<= n 0) 1 (* n (f (- n 1)))))

Вычисляет истинную анонимную рекурсивную факториальную функцию, которая может, например, использоваться как:

(let ;no letrec used
    ((factorial (lambdarec (f n) (if (<= n 0) 1 (* n (f (- n 1)))))))
  (factorial 4)) ; ===> 24

Однако истинная причина, которая делает ее анонимной, заключается в том, что если я это сделаю:

((lambdarec (f n) (if (<= n 0) 1 (* n (f (- n 1))))) 4)

Затем функция очищается из памяти и не имеет области видимости, поэтому после этого:

(f 4)

Будут либо сигнализировать об ошибке, либо будут связаны с тем, что раньше было связано с.

В Haskell особый способ достичь такого же результата будет:

\n -> let fac x = if x<2 then 1 else fac (x-1) * x
    in fac n

Разница опять же в том, что эта функция не имеет области видимости, если я ее не использую, поскольку Haskell Lazy эффект такой же, как и пустая строка кода, он действительно литерал, поскольку он имеет тот же эффект, что и C код:

3;

Литеральное число. И даже если я буду использовать его сразу после этого, он уйдет. Это то, что буквальные функции, а не создание во время выполнения как таковое.

Ответ 16

Clojure может это сделать, поскольку fn использует для этого необязательное имя (имя не выходит за пределы области определения):

> (def fac (fn self [n] (if (< n 2) 1 (* n (self (dec n))))))
#'sandbox17083/fac
> (fac 5)
120
> self
java.lang.RuntimeException: Unable to resolve symbol: self in this context

Если это рекурсия хвоста, то recur - гораздо более эффективный метод:

> (def fac (fn [n] (loop [count n result 1]
                     (if (zero? count)
                       result
                       (recur (dec count) (* result count))))))