Как Ваш любимый язык обрабатывает глубокую рекурсию? [закрытый]

Если вы хотите использовать pd.get_dummies, есть возможность итеративно включать ваши кодировки для каждой партии.

Для вашей первой партии:

ohe = pd.get_dummies(data_rest, columns=['label_col'])

Для каждой последующей партии:

for b in batches:
    batch_ohe = pd.get_dummies(b, columns=['label_col'])
    ohe = pd.concat([ohe, batch_ohe], axis=0)

ohe = ohe.fillna(0)
21
задан jettero 24 October 2008 в 13:21
поделиться

10 ответов

"Люди Python быстры, чтобы указать, что можно всегда преобразовывать рекурсивные функции в повторяющиеся и что они всегда быстрее",

Это верно, но если действительно столь же легко как все это, почему Python не делает этого для меня, так, чтобы мой код мог выглядеть максимально простым? (Я говорю это для не хлопанья лицами, осуществляющими внедрение Python, но потому что ответ объясняет проблему).

оптимизации Рекурсии присутствовали на функциональных языках с тех пор, как, 14-й век или что-то. Haskell, CAML, реализации Lisp все обычно преобразовывают, по крайней мере, рекурсивные функции хвоста в повторения: Вы в основном делаете это путем определения этого, это возможно, т.е. что функция может быть перестроена таким образом, что никакие локальные переменные кроме возвращаемого значения не используются после рекурсивного вызова. Один прием, чтобы позволить, если существует некоторая работа, сделанная к рекурсивно вызванному возвращаемому значению перед возвратом, должен представить дополнительный параметр "аккумулятора". Простыми словами это означает, что работа может эффективно быть сделана на пути "вниз" вместо на пути: поиск вокруг, 'как сделать функцию рекурсивной хвостом' для деталей.

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

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

Memoization является полезной техникой, тем не менее, для произвольно рекурсивных функций, которые Вы хотели бы искать, если Вы интересуетесь возможными подходами. То, что это означает, - то, что каждый раз функция оценена, Вы засовываете результат в кэш. Для использования этого для оптимизации рекурсии, в основном, если рекурсивная функция считает в обратном порядке, и Вы memoize это, тогда можно оценить его многократно путем добавления цикла, который подсчитывает вычисление каждого значения функции в свою очередь, пока Вы не достигаете цели. Это использует очень мало стекового пространства при условии, что кэш записки является достаточно большим для содержания всех значений, в которых Вы будете нуждаться: например, если f (n) зависит от f (n-1), f (n-2) и f (n-3), Вам только нужно пространство для 3 значений в кэше: когда Вы поднимаетесь, можно отшвырнуть лестничную структуру ногой. Если f (n) зависит от f (n-1) и f (n/2), Вам нужно много пространства в кэше, но еще меньше, чем Вы использовали бы для стека в неоптимизированной рекурсии.

21
ответ дан 29 November 2019 в 20:09
поделиться

Это - больше вопроса о реализации, чем вопрос о языке. Нет ничего останавливающего некоторого (stoopid) конструктора компилятора C от также ограничения их стека вызовов к 1 000. Существует много маленьких процессоров там, которые не имели бы стекового пространства для даже что многие.

(Люди Python быстры, чтобы указать, что можно всегда преобразовывать рекурсивные функции в повторяющиеся и что они всегда быстрее. Это на 100% верно. Это не действительно, о чем мой вопрос все же.)

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

может иметь место, что повторяющееся решение / решение для стека всегда быстрее в Python . Если так, это - сбой Python, не рекурсии.

7
ответ дан 29 November 2019 в 20:09
поделиться

PHP имеет предел по умолчанию 100, прежде чем он умрет:

Fatal error: Maximum function nesting level of '100' reached, aborting!

Редактирование: можно изменить предел с ini_set('xdebug.max_nesting_level', 100000);, но если Вы выходите за предел приблизительно 1 150 повторений катастрофические отказы PHP:

[Fri Oct 24 11:39:41 2008] [notice] Parent: child process exited with status 3221225477 -- Restarting.

4
ответ дан 29 November 2019 в 20:09
поделиться

C#/.NET будет использовать хвостовую рекурсию в определенном наборе обстоятельств. (Компилятор C# не испускает tailcall код операции, но , JIT реализует хвостовую рекурсию в некоторых случаях .

у Шри Borde также есть сообщение по этой теме . Конечно, CLR постоянно изменяется, и с.NET 3.5, и 3.5SP1 это, возможно, изменилось снова относительно последних вызовов.

3
ответ дан 29 November 2019 в 20:09
поделиться

Используя следующее в интерактивной консоли F#, это работало в меньше, чем секунда:

let rec f i l = 
  match i with 
  | i when i < l -> f (i+1) l
  | _ -> l

f 0 100000000;;

я тогда попробовал прямой перевод т.е.

let rec g i l = if i < l then g (i+1) l else l

g 0 100000000;;

Тот же результат, но различная компиляция.

Это - то, в чем похож f, когда переведено в C#:

int f(int i, int l)
{
  while(true)
  {
    int num = i;
    if(num >= l)
      return l;
    int i = num;
    l = l;
    i = i + 1;
  }
}

g, однако переводится в это:

int g(int i, int l)
{
  while(i < l)
  {
    l = l;
    i++;
  }
  return l;
}

интересно, что две функции, которые являются существенно тем же, представляются по-другому компилятором F#. Это также показывает, что компилятор F# имеет рекурсивную хвостом оптимизацию. Таким образом это должно циклично выполниться, пока я не достигаю предела для 32-разрядных целых чисел.

3
ответ дан 29 November 2019 в 20:09
поделиться

Согласно этому потоку, приблизительно 5 000 000 с Java, RAM на 1 ГБ. (и что, с 'клиентской' версией горячей точки)

, Который был с стек (-Xss) из 300 мес.

С - параметр сервера , который мог быть увеличен.

Также можно попытаться оптимизировать компилятор ( со СТРУЕЙ , например) для сокращения стека наверху на каждом слое.

2
ответ дан 29 November 2019 в 20:09
поделиться

В некоторых не патологические случаи (как Ваш), (последний) Lua будет использовать рекурсия последнего вызова , т.е. это просто перейдет, не продвигая данные в стеке. Таким образом, количество циклов рекурсии может быть почти неограниченным.

Протестированный с:

function f(i, l)
    if i < l then
        return f(i+1, l)
    end
    return i
end

local val1  = arg[1] or 1
local val2  = arg[2] or 100000000
print(f(val1 + 0, val2 + 0))

Также с:

function g(i, l)
    if i >= l then
        return i
    end
    return g(i+1, l)
end

и даже попробованная перекрестная рекурсия (f звонящий g и g, звонящий f...).

В Windows, Lua 5.1 использует приблизительно 1.1 МБ (постоянные) для выполнения этого, концов за несколько секунд.

2
ответ дан 29 November 2019 в 20:09
поделиться

Визуальный Dataflex будет переполнение стека.

1
ответ дан 29 November 2019 в 20:09
поделиться

Я - настоящий поклонник функционального программирования, и так как большинство тех языков реализует оптимизацию последнего вызова, можно рекурсивно вызвать столько, сколько Вам нравится:-P

Однако практически, я должен использовать много Java и использовать Python много также. Никакая идея, что предельный Java имеет, но для Python, который я на самом деле запланировал (но еще не сделали его) реализовать декоратора, который был бы последний вызов оптимизировать украшенную функцию. Я планировал это для не оптимизации рекурсии, но главным образом как упражнение в динамичном исправлении байт-кода Python и получении дополнительной информации о внутренностях Python. Вот некоторые интересные ссылки: http://lambda-the-ultimate.org/node/1331 и http://www.rowehl.com/blog/?p=626

1
ответ дан 29 November 2019 в 20:09
поделиться

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

sub f{
  if( $_[0] < $_[1] ){

    # return f( $_[0]+1, $_[1] );

    @_ = ( $_[0]+1, $_[1] );
    goto &f;

  } else {
    return $_[0]
  }
}

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


Вы могли также использовать Sub:: Звоните:: Повторитесь модуль. Который делает код легче понять, и короче.

use Sub::Call::Recur;
sub f{
  recur( $_[0]+1, $_[1] ) if $_[0] < $_[1];
  return $_[0];
}
1
ответ дан 29 November 2019 в 20:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: