Spec-Zone.ru › Perl 5.36

Memoize

СОДЕРЖАНИЕ

  • ИМЯ
  • СИНТАКСИС
  • ОПИСАНИЕ
  • ДЕТАЛИ
  • НАСТРОЙКИ
    • УСТАНОВКА
    • НОРМАЛИЗАТОР
    • SCALAR_CACHE, LIST_CACHE
      • Значения списков в скалярном контексте
      • Объединённые кэши на диске
  • ДРУГИЕ ВОЗМОЖНОСТИ
    • unmemoize
    • flush_cache
  • ОГРАНИЧЕНИЯ
  • ПОДДЕРЖКА ПОСТОЯННОГО КЭША
  • ПОДДЕРЖКА ИСТЕЧЕНИЯ СРОКА ДЕЙСТВИЯ
  • ОШИБКИ
  • СПИСОК РАССЫЛКИ
  • АВТОР
  • АВТОРСКИЕ ПРАВА И ЛИЦЕНЗИЯ
  • БЛАГОДАРНОСТИ

ИМЯ

Memoize - Ускоряет функции, обменивая память на время

СИНТАКСИС

# This is the documentation for Memoize 1.03
use Memoize;
memoize('slow_function');
slow_function(arguments);    # Is faster than it was before

Обычно этого достаточно. Однако доступно множество настроек:

memoize(function, options...);

Настройки включают:

NORMALIZER => function
INSTALL => new_name

SCALAR_CACHE => 'MEMORY'
SCALAR_CACHE => ['HASH', \%cache_hash ]
SCALAR_CACHE => 'FAULT'
SCALAR_CACHE => 'MERGE'

LIST_CACHE => 'MEMORY'
LIST_CACHE => ['HASH', \%cache_hash ]
LIST_CACHE => 'FAULT'
LIST_CACHE => 'MERGE'

ОПИСАНИЕ

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

Вот пример. Рассмотрим последовательность Фибоначчи, определённую следующей функцией:

# Compute Fibonacci numbers
sub fib {
  my $n = shift;
  return $n if $n < 2;
  fib($n-1) + fib($n-2);
}

Эта функция очень медленная. Почему? Чтобы вычислить fib(14), она сначала вычисляет fib(13) и fib(12), а затем складывает результаты. Но для вычисления fib(13), она сначала должна вычислить fib(12) и fib(11), а затем вычисляет fib(12) заново, хотя ответ тот же. И каждый раз, когда она хочет вычислить fib(12), она должна вычислить fib(11) заново, и так далее. Эта функция так много перевычисляет старые результаты, что выполняется очень долго — fib(14) делает 1200 дополнительных рекурсивных вызовов, чтобы вычислить и перевычислить уже вычисленные значения.

Эта функция хорошо подходит для запоминания. Если вы запоминаете функцию `fib` выше, она вычислит fib(14) только один раз, когда ей это нужно впервые, а затем сохранит результат в таблице. Затем, если вы запросите fib(14) ещё раз, она вернёт результат из таблицы. Во время вычисления fib(14), вместо того, чтобы вычислять fib(12) дважды, она делает это один раз; второй раз, когда ей нужно значение, она берёт его из таблицы. Она не вычисляет fib(11) четыре раза, она вычисляет его один раз, а потом берёт его из таблицы ещё три раза. Вместо того, чтобы делать 1200 рекурсивных вызовов функции `fib`, она делает 15. Это делает функцию примерно в 150 раз быстрее.

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

# Compute Fibonacci numbers, memoized version
{ my @fib;
  sub fib {
    my $n = shift;
    return $fib[$n] if defined $fib[$n];
    return $fib[$n] = $n if $n < 2;
    $fib[$n] = fib($n-1) + fib($n-2);
  }
}

Или вы можете использовать этот модуль, как это:

use Memoize;
memoize('fib');

# Rest of the fib function just like the original version.

Это делает запоминание простым.

Вот ещё один простой пример: я написал простой трассировщик лучей; программа смотрит в определённом направлении, определяет, что она видит, а затем преобразует значение `color` (обычно строка, например, `red`) этого объекта в значение красного, зелёного и синего пикселя, вот так:

for ($direction = 0; $direction < 300; $direction++) {
  # Figure out which object is in direction $direction
  $color = $object->{color};
  ($r, $g, $b) = @{&ColorToRGB($color)};
  ...
}

Так как в изображении относительно мало объектов, есть только несколько цветов, которые многократно ищут. Запоминание ColorToRGB ускорило программу на несколько процентов.

ДЕТАЛИ

Этот модуль экспортирует ровно одну функцию, memoize. Остальные функции в этом пакете — не ваше дело.

Вы должны сказать

memoize(function)

где function — имя функции, которую нужно запомнить, или ссылка на неё. memoize возвращает ссылку на новую, запомненную версию функции или undef при ошибке. В настоящее время нет ошибок, но они могут появиться в будущем.

Если function было именем функции, то memoize скрывает старую версию и устанавливает новую запоминающую версию под старым именем, так что &function(...) на самом деле вызывает запоминающую версию.

НАСТРОЙКИ

Есть некоторые необязательные настройки, которые можно передать memoize , чтобы изменить её поведение. Чтобы передать настройки, вызовите memoize так:

memoize(function, NORMALIZER => function,
                  INSTALL => newname,
                  SCALAR_CACHE => option,
                  LIST_CACHE => option
                 );

Каждая из этих настроек необязательна; вы можете включить некоторые, все или ни одну из них.

УСТАНОВКА

Если вы передадите имя функции с INSTALL, Memoize установит новую, запоминающую версию функции под указанным именем. Например,

memoize('fib', INSTALL => 'fastfib')

устанавливает запоминающую версию fib как fastfib; без опции INSTALL она бы заменила старую fib запоминающей версией.

Чтобы предотвратить memoize от установки запоминающей версии где-либо, используйте INSTALL => undef.

НОРМАЛИЗАТОР

Предположим, ваша функция выглядит так:

# Typical call: f('aha!', A => 11, B => 12);
sub f {
  my $a = shift;
  my %hash = @_;
  $hash{B} ||= 2;  # B defaults to 2
  $hash{C} ||= 7;  # C defaults to 7

  # Do something with $a, %hash
}

Теперь следующие вызовы вашей функции полностью эквивалентны:

f(OUCH);
f(OUCH, B => 2);
f(OUCH, C => 7);
f(OUCH, B => 2, C => 7);
f(OUCH, C => 7, B => 2);
(etc.)

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

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

sub normalize_f {
  my $a = shift;
  my %hash = @_;
  $hash{B} ||= 2;
  $hash{C} ||= 7;

  join(',', $a, map ($_ => $hash{$_}) sort keys %hash);
}

Каждый из списков аргументов после работы функции normalize_f выглядит совершенно одинаково, как это:

OUCH,B,2,C,7

Вы бы сказали Memoize использовать этот нормализатор так:

memoize('f', NORMALIZER => 'normalize_f');

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

По умолчанию нормализатор просто конкатенирует аргументы с символом 28 между ними. (В ASCII это называется FS или control-\.) Это всегда работает правильно для функций с одним строковым аргументом, а также когда аргументы никогда не содержат символ 28. Однако это может спутать некоторые списки аргументов:

normalizer("a\034", "b")
normalizer("a", "\034b")
normalizer("a\034\034b")

например.

Так как ключи хеша — это строки, по умолчанию нормализатор не будет различать undef и пустую строку. Он также не будет работать, когда аргументы функции являются ссылками. Например, рассмотрим функцию g, которая получает два аргумента: число и ссылку на массив чисел:

g(13, [1,2,3,4,5,6,7]);

По умолчанию нормализатор преобразует это во что-то вроде "13\034ARRAY(0x436c1f)". Это было бы нормально, за исключением того, что последующий массив чисел может храниться в другом месте, даже если он содержит те же данные. Если это происходит, Memoize будет считать, что аргументы разные, хотя они эквивалентны. В этом случае подходящим нормализатором будет:

sub normalize { join ' ', $_[0], @{$_[1]} }

Для примера выше это даёт ключ «13 1 2 3 4 5 6 7».

Ещё одно применение нормализаторов — когда функция зависит от данных, отличных от тех, которые содержатся в её аргументах. Предположим, у вас есть функция, которая возвращает значение, зависящее от текущего часа дня:

sub on_duty {
  my ($problem_type) = @_;
  my $hour = (localtime)[2];
  open my $fh, "$DIR/$problem_type" or die...;
  my $line;
  while ($hour-- > 0)
    $line = <$fh>;
  } 
  return $line;
}

В 10:23 эта функция генерирует 10-ю строку файла данных; в 15:45 она генерирует 15-ю строку вместо этого. По умолчанию Memoize увидит только аргумент $problem_type. Чтобы исправить это, включите текущий час в нормализатор:

sub normalize { join ' ', (localtime)[2], @_ }

Контекст вызова функции (скалярный или список) передаётся нормализатору. Это означает, что если запоминаемая функция будет обрабатывать свои аргументы по-разному в контексте списка по сравнению с контекстом скаляра, вы можете заставить функцию нормализации выбирать своё поведение на основе результатов wantarray. Даже если вызвана в контексте списка, нормализатор всё равно должен вернуть одну строку.

SCALAR_CACHE, LIST_CACHE

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

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

Аргумент для LIST_CACHE или SCALAR_CACHE должен быть одной из следующих четырёх строк:

MEMORY
FAULT
MERGE
HASH

или же это должна быть ссылка на массив, первым элементом которого является одна из этих четырёх строк, например [HASH, arguments...].

MEMORY

MEMORY означает, что возвращаемые значения из функции будут кэшированы в обычной переменной Perl-хэша. Переменная хэша не сохранится после завершения программы. Это значение по умолчанию.

HASH

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

Связанный хэш может иметь любые семантики. Обычно он связан с базой данных на диске, так что кэшированные значения хранятся в базе данных и извлекаются из неё при необходимости, а файл диска обычно сохраняется после завершения вашей программы. Смотрите perltie для более подробных сведений о tie.

Типичный пример:

use DB_File;
tie my %cache => 'DB_File', $filename, O_RDWR|O_CREAT, 0666;
memoize 'function', SCALAR_CACHE => [HASH => \%cache];

Это имеет эффект хранения кэша в базе данных DB_File, имя которой находится в $filename. Кэш сохранится после завершения программы. В следующий раз, когда программа запустится, она найдёт кэш, уже заполненный из предыдущего запуска программы. Или вы можете принудительно заполнить кэш, создав пакетную программу, которая работает в фоновом режиме и заполняет файл кэша. Затем, когда вы запустите свою реальную программу, мемоизированная функция будет быстрой, потому что все её результаты были предварительно вычислены.

Ещё одна причина использования HASH — предоставить свою собственную переменную хэша. Затем вы можете просматривать или изменять содержимое хэша, чтобы получить более точный контроль над управлением кэшем.

TIE

Этот параметр больше не поддерживается. Он всё ещё документирован только для помощи в отладке старых программ, которые его используют. Старые программы следует преобразовать, чтобы использовать вместо него параметр HASH.

memoize ... ['TIE', PACKAGE, ARGS...]

является всего лишь сокращением для

require PACKAGE;
{ tie my %cache, PACKAGE, ARGS...;
  memoize ... [HASH => \%cache];
}
FAULT

FAULT означает, что вы никогда не ожидаете вызова функции в скалярном (или списочном) контексте, и что если Memoize обнаружит такой вызов, он должен прервать выполнение программы. Сообщение об ошибке — одно из

`foo' function called in forbidden list context at line ...
`foo' function called in forbidden scalar context at line ...
MERGE

MERGE обычно означает, что мемоизированная функция не различает контекст списка и скаляра, и что возвращаемые значения в обоих контекстах должны храниться вместе. И LIST_CACHE => MERGE, и SCALAR_CACHE => MERGE означают то же самое.

Рассмотрим эту функцию:

sub complicated {
  # ... time-consuming calculation of $result
  return $result;
}

Функция complicated вернёт то же числовое $result независимо от того, вызывается ли она в списочном или скалярном контексте.

Обычно следующий код приведёт к двум вызовам функции complicated, даже если complicated мемоизирована:

$x = complicated(142);
($y) = complicated(142);
$z = complicated(142);

Первый вызов кэширует результат, скажем, 37, в скалярном кэше; второй — список (37) в списочном кэше. Третий вызов не вызывает реальную функцию complicated; он получает значение 37 из скалярного кэша.

Очевидно, что второй вызов complicated — пустая трата времени, а хранение его возвращаемого значения — пустая трата места. Указание LIST_CACHE => MERGE заставит memoize использовать один и тот же кэш для возвращаемых значений скалярного и списочного контекста, так что второй вызов будет использовать скалярный кэш, заполненный первым вызовом. complicated вызывается только один раз, и оба последующих вызова возвращают 3 из кэша, независимо от вызывающего контекста.

Список значений в скалярном контексте

Рассмотрим эту функцию:

sub iota { return reverse (1..$_[0]) }

Эта функция обычно возвращает список. Предположим, что вы её мемоизировали и объединили кэши:

memoize 'iota', SCALAR_CACHE => 'MERGE';

@i7 = iota(7);
$i7 = iota(7);

Здесь первый вызов кэширует список (1,2,3,4,5,6,7). Второй вызов не имеет смысла. Memoize не может угадать, какое поведение должно иметь iota в скалярном контексте, не вызвав его в скалярном контексте. Обычно Memoize вызывало бы iota в скалярном контексте и кэшировало бы результат, но параметр SCALAR_CACHE => 'MERGE' говорит не делать этого, а использовать значение кэшированного списка в контексте списка. Но оно не может вернуть список из семи элементов в скалярном контексте. В этом случае $i7 получит первый элемент кэшированного значения списка, а именно 7.

Объединённые кэши на диске

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

tie my %cache => 'MLDBM', 'DB_File', $filename, ...;

memoize 'myfunc',
  NORMALIZER => 'n',
  SCALAR_CACHE => [HASH => \%cache],
  LIST_CACHE => 'MERGE',
;

sub n {
  my $context = wantarray() ? 'L' : 'S';
  # ... now compute the hash key from the arguments ...
  $hashkey = "$context:$hashkey";
}

Эта функция-нормализатор будет хранить значения скалярного контекста в файле на диске под ключами, начинающимися с S:, а значения контекста списка — под ключами, начинающимися с L:.

ДРУГИЕ ВОЗМОЖНОСТИ

unmemoize

Есть функция unmemoize, которую вы можете импортировать, если хотите. Зачем это нужно? Вот пример: предположим, что ваш кэш привязан к файлу DBM, и вы хотите убедиться, что кэш записывается на диск, если кто-то прерывает выполнение программы. Если программа завершается нормально, это произойдёт в любом случае, но если кто-то нажмёт Ctrl+C или что-то в этом роде, то программа завершится немедленно, не синхронизируя базу данных. Поэтому вы можете сделать следующее:

$SIG{INT} = sub { unmemoize 'function' };

unmemoize принимает ссылку на, или имя, ранее мемоизированной функции и отменяет всё, что она сделала, чтобы предоставить мемоизированную версию в первую очередь, включая приведение имени к немемоизированной версии при необходимости. Возвращает ссылку на немемоизированную версию функции.

Если вы попросите её размемоизировать функцию, которая никогда не была мемоизирована, она выдаст ошибку.

flush_cache

flush_cache(function) очистит кэши, удалив все кэшированные данные. Аргумент может быть именем функции или ссылкой на функцию. Для более точного контроля над временем удаления или истечения срока действия данных смотрите документацию по Memoize::Expire, включённой в этот пакет.

Обратите внимание, что если кэш является связанным хэшем, flush_cache попытается вызвать метод CLEAR в хэше. Если метода CLEAR нет, это приведёт к ошибке во время выполнения.

Альтернативный подход к очистке кэша — использовать параметр HASH (см. выше), чтобы запросить у Memoize использовать определённую переменную хэша в качестве кэша. Затем вы можете в любое время просматривать или изменять хэш любым желаемым способом. Вы можете очистить кэш, используя %hash = ().

ОСОБЕННОСТИ

Мемоизация не является панацеей:

  • Не мемоизируйте функцию, поведение которой зависит от состояния программы, помимо её собственных аргументов, таких как глобальные переменные, время суток или ввод из файла. Эти функции не будут давать правильных результатов, когда будут мемоизированы. Пример:

    sub f {
      time;
    }

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

  • Не мемоизируйте функцию со побочными эффектами.

    sub f {
      my ($a, $b) = @_;
      my $s = $a + $b;
      print "$a + $b = $s.\n";
    }

    Эта функция принимает два аргумента, складывает их и выводит их сумму. Её возвращаемое значение — число напечатанных символов, но вам, вероятно, это не нужно. Но Memoize этого не понимает. Если вы мемоизируете эту функцию, вы получите ожидаемый результат в первый раз, когда попросите её напечатать сумму 2 и 3, но последующие вызовы вернут 1 (возвращаемое значение print) без фактического вывода чего-либо.

  • Не мемоизируйте функцию, которая возвращает структуру данных, которая модифицируется её вызывающей стороной.

    Рассмотрим эти функции: getusers возвращает список пользователей каким-то образом, а затем main выбрасывает первого пользователя из списка и выводит остальных:

    sub main {
      my $userlist = getusers();
      shift @$userlist;
      foreach $u (@$userlist) {
        print "User $u\n";
      }
    }
    
    sub getusers {
      my @users;
      # Do something to get a list of users;
      \@users;  # Return reference to list.
    }

    Если вы мемоизируете getusers здесь, она будет работать правильно только один раз. Ссылка на список пользователей будет храниться в таблице мемо. main удалит первый элемент из ссылаемого списка. В следующий раз, когда вы вызовете main, Memoize не вызовет getusers; она просто вернёт ту же ссылку на тот же список, который она получила в последний раз. Но на этот раз список уже потерял свою голову; main ошибочно удалит ещё один элемент из него. Список будет становиться короче с каждым вызовом main.

    Точно так же, это:

    $u1 = getusers();    
    $u2 = getusers();    
    pop @$u1;

    будет изменять $u2, а также $u1, потому что обе переменные ссылаются на один и тот же массив. Если бы getusers не была мемоизирована, $u1 и $u2 ссылались бы на разные массивы.

  • Не мемоизируйте очень простую функцию.

    Недавно кто-то сказал мне, что модуль Memoize замедлил его программу вместо ускорения. Оказалось, что он мемоизировал следующую функцию:

    sub square {
      $_[0] * $_[0];
    }

    Я указал, что Memoize использует хэш, и что поиск числа в хэше обязательно займёт гораздо больше времени, чем одно умножение. Нет способа ускорить функцию square.

    Мемоизация — не волшебство.

ПОДДЕРЖКА ПОСТОЯННОГО КЭША

Вы можете связать таблицы кэша с любым типом связанного хэша, который вы хотите, если он поддерживает TIEHASH, FETCH, STORE, и EXISTS. Например,

tie my %cache => 'GDBM_File', $filename, O_RDWR|O_CREAT, 0666;
memoize 'function', SCALAR_CACHE => [HASH => \%cache];

работает нормально. Для некоторых методов хранения вам нужен небольшой связующий код.

SDBM_File не предоставляет метод EXISTS, поэтому в этот пакет включён модуль-«склейка» под названием Memoize::SDBM_File, который его предоставляет. Используйте его вместо обычного SDBM_File для хранения вашей таблицы кэша на диске в базе данных SDBM_File:

tie my %cache => 'Memoize::SDBM_File', $filename, O_RDWR|O_CREAT, 0666;
memoize 'function', SCALAR_CACHE => [HASH => \%cache];

NDBM_File имеет ту же проблему и то же решение. (Используйте Memoize::NDBM_File instead of plain NDBM_File.)

Storable — это вообще не класс связанного хэша. Вы можете использовать его для хранения хэша на диске и повторного извлечения его, но вы не можете изменять хэш, пока он находится на диске. Итак, если вы хотите хранить таблицу кэша в базе данных Storable, используйте Memoize::Storable, который помещает интерфейс типа хэша на Storable. Таблица хэша фактически хранится в памяти и загружается из вашего файла Storable в момент мемоизации функции и сохраняется обратно в момент её размемоизации (или при завершении вашей программы):

tie my %cache => 'Memoize::Storable', $filename;
memoize 'function', SCALAR_CACHE => [HASH => \%cache];

tie my %cache => 'Memoize::Storable', $filename, 'nstore';
memoize 'function', SCALAR_CACHE => [HASH => \%cache];

Включите опцию `nstore`, чтобы база данных Storable записывалась в «сетевом порядке». (См. Storable для получения более подробной информации об этом.)

Функция flush_cache() вызовет ошибку во время выполнения, если связанный пакет не предоставляет метод CLEAR.

ПОДДЕРЖКА ИСТЕЧЕНИЯ СРОКА ДЕЙСТВИЯ

См. Memoize::Expire, это плагин-модуль, который добавляет функциональность истечения срока действия в Memoize. Если вам не нравятся политики, реализованные в Memoize::Expire, легко написать свой собственный плагин-модуль для реализации любой желаемой политики. Memoize поставляется с несколькими примерами. Менеджер истечения срока действия, реализующий политику LRU, доступен на CPAN как Memoize::ExpireLRU.

ОШИБКИ

Набор тестов значительно улучшен, но всегда нуждается в усовершенствовании.

Существует проблема с работой goto &f в многопоточном Perl, возможно, из-за лексического охватывания @_. Это ошибка в Perl, и до её решения кешированные функции будут видеть немного отличающееся caller() и будут работать немного медленнее в многопоточных Perl, чем в однопоточных.

Некоторые версии DB_File не позволят вам сохранить данные под ключом нулевой длины. Это означает, что если у вас есть функция f, которую вы кэшировали, и кэш находится в базе данных DB_File, то значение f() (f вызвана без аргументов) не будет кэшироваться. Если это большая проблема, вы можете предоставить функцию нормализации, которая добавляет "x" к каждому ключу.

СПИСОК РАССЫЛКИ

Чтобы присоединиться к списку рассылки с очень низкой активностью для анонсов о Memoize, отправьте пустое сообщение на mjd-perl-memoize-request@plover.com.

АВТОР

Марк-Джейсон Доминус (mjd-perl-memoize+@plover.com), компания Plover Systems.

Смотрите страницу Memoize.pm на http://perl.plover.com/Memoize/ для новостей и обновлений. Вблизи этой страницы, на http://perl.plover.com/MiniMemoize/ есть статья о мемоизации и внутренних механизмах Memoize, опубликованная в журнале The Perl Journal, выпуск #13. (Эта статья также включена в дистрибутив Memoize как `article.html`.)

Книга автора Higher-Order Perl (2005, ISBN 1558607013, издательство Morgan Kaufmann) подробно рассматривает мемоизацию (и многие другие темы). Она доступна онлайн бесплатно. Для получения дополнительной информации посетите http://hop.perl.plover.com/ .

Чтобы присоединиться к списку рассылки для анонсов о Memoize, отправьте пустое сообщение на mjd-perl-memoize-request@plover.com. Этот список рассылки предназначен только для анонсов и имеет очень низкую активность — меньше двух сообщений в год.

АВТОРСКИЕ ПРАВА И ЛИЦЕНЗИЯ

Copyright 1998, 1999, 2000, 2001, 2012 Марка Джейсона Доминуса

Эта библиотека является свободным программным обеспечением; вы можете распространять и/или изменять её на тех же условиях, что и сам Perl.

БЛАГОДАРНОСТИ

Большое спасибо Флориану Рагвицу за помощь в администрировании и упаковке, Джону Тромпу за сообщения об ошибках, Джонатану Рою за сообщения об ошибках и предложения, Майклу Шверну за другие сообщения об ошибках и исправления, Майку Кариасо за помощь в определении правильного решения по истечению срока действия, Джошуа Герту, Джошуа Чамасу, Джонатану Рою (еще раз), Марку Д. Андерсону и Эндрю Джонсону за дополнительные предложения по истечению срока действия, Бренту Пауэрсу за модуль Memoize::ExpireLRU, Ариэлю Скольникову за приятные сообщения о функции Фибоначчи, Диону Альмаеру за стимулирующие предложения по функции нормализации по умолчанию, Уолту Манковскому и Курту Старсинику за большую помощь в исследовании проблем в многопоточном Perl, Алексу Дудкевичу за сообщение об ошибке в типизированных функциях и за проверку моего исправления, Тони Бассу за много полезных предложений, Джонатану Рою (еще раз) за нахождение применения unmemoize(), Филиппу Вердре за исчерпывающее обсуждение Hook::PrePostCall, Нату Торкингтону за советы, которые я проигнорировал, Крису Нандору за советы по переносимости, Рэнделлу Шварцу за предложение функции 'flush_cache', и Дженде Криницьки за свет в этом мире.

Особая благодарность Яркко Хетениеми, автору 5.8.0, за включение этого модуля в ядро и за его терпеливое и полезное руководство во время процесса интеграции.

2 ошибки POD

При парсинге POD были обнаружены следующие ошибки:

Около строки 755:

Вы забыли '=back' перед '=head3'

Около строки 804:

=back без =over

© 1993–2021 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.36.0/Memoize

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API