Еще парочка недавно опубликованных фундаментальных глюков
Mathematica (проверил присутствие в версиях 5.2 и 7.0.1, но, вероятно, они есть во всех версиях, где есть данные функции).
Глюк первый:
"зомби" вылезают из Module[]! Как вы думаете, почему
MathKernel.exe, даже при выставленном
$HistoryLength = 0
, все равно в процессе вычислений (даже если вызывается одна и та же функция, а результаты её работы только экспортируются в файл) постоянно накапливает оперативную память, которую нельзя сбросить (и даже замедлить её наращивание) никакими встроенными функциями? Отчасти на этот вопрос проливает свет недавно обнаруженный баг функции Module[], введенной в версии 2 как альтернатива кривоватому Block[]. Так вот, оказывается, эта функция с легкостью может начать генерировать при каждом её вызове "вечные" мусорные переменные с атрибутом Temporary, в совершенно неограниченном количестве. Причем к каждой такой переменной могут быть привязаны ещё её Definition[] и Attributes[]. Вот уж пример универсальности поговорки: "Нет ничего более постоянного, чем временное (Temporary)!"
Рассмотрим "нормальное" поведение Module[]. Определим простую функцию:
[no]
In[1]:=
a[b_] := Module[{c, d}, c := 1; d := 9; d];[/no]
Теперь посмотрим, какие переменные появились в текущем контексте:
[no]In[2]:=
Names[$Context<>"*"]
Out[2]=
{a,b,c,d}[/no]
Это - "нормально", т.е. by design.
А теперь введем внутрь Module[] условие (Condition[]):
[no]
In[1]:=
a[b_] := Module[{c, d}, c := 1; d := 9; d /; b === 1];
[/no]
Что же мы имеем теперь в текущем контексте?
[no]In[2]:=
Names[$Context<>"*"]
Out[2]=
{a,b,c,c$,d,d$}[/no]
При введении Condition ("/;") появилась мусорная переменная "c$" c атрибутом
Temporary, которая к тому же отказывается удаляться через[no] Remove[c$][/no]. Но она хотя бы не размножается. Переменная "d$" вроде как и правда должна появляться из-за Condition.
А теперь посмотрим, что происходит при вызове функции, если условие выполняется:
[no]
In[3]:=
a[1];
Names[$Context <> "*"]
{#,ToString[Definition[#]]}&/@Select[%,MemberQ[Attributes[#],Temporary]&]//ColumnForm
Out[4]=
{a,b,c,c$,d,d$,d$16}
Out[5]=
{"c$","Attributes[c$] = {Temporary}"}
{"d$","Attributes[d$] = {Temporary}"}
{"d$16","Attributes[d$16] = {Temporary}"}[/no]
Если условие в Condition выполняется, происходит генерация "зомби" - мусорных переменных с атрибутом Temporary, которые, согласно документации, должны автоматически удаляться после завершения выполнения Module[]. При каждом выполнении Module[] генерируется новая такая переменная, причем по завершении работы Module[] она не удаляется:
[no]
In[6]:=
Table[a[1],{1000}];
Length@Names[$Context <> "*"]
Out[7]=
1007[/no]
Надо, правда, признать, что при выставлении $HistoryLength = 0 такое поведение воспроизводится только в том случае, если все вызовы a[1] происходили в одной и той же ячейке (см. последний пример). При выполнении новой ячейки лишние мусорные переменные (кроме c$) и в самом деле удаляются, если $HistoryLength = 0. Однако в практических задачах все вызовы как раз и происходят в пределах одной ячейки, поэтому данный баг вылезает "во всей красе". Следует также напомнить, что Module[] активно используется не только в дополнительных пакетах, поставляемых с системой, но и, несомненно, в высокоуровневом коде втроенных в ядро функций.
===============================================
Глюк второй:
"шутливый" Compile[].
На этот раз - рандомные результаты работы скомпилированной функции в версиях 5.2 и 7.0.1 (в 8.0 при компиляции возникает runtime error).
Вот несколько функций, которые должны возвращать одно и то же. Однако первая из них (самая простая) оказывается с "чувством юмора":
abc1 = Compile[{}, Module[{a, b, c}, a = 1.0;
b = {1.0, 2.0};
c = {{1.0, 2.0}, {3.0, 4.0}};
a*b*c]];
abc2 = Compile[{}, Module[{a, b, c}, a = 1.0;
b = {1.0, 2.0};
c = {{1.0, 2.0}, {3.0, 4.0}};
(a*b)*c]];
abc3 = Compile[{}, Module[{a, b, c}, a = 1.0;
b = {1.0, 2.0};
c = {{1.0, 2.0}, {3.0, 4.0}};
a*(b*c)]];
Попробуйте теперь несколько раз последовательно выполнить ячейку:
abc1[]
abc2[]
abc3[]
и сравните результаты!