Ответ 1
Что касается производительности (с момента ответа на ваш первый вопрос) - всеми способами выполняйте проверки, но в своих функциях верхнего уровня (которые получают данные непосредственно от пользователя вашей функциональности. Пользователь также может быть другой независимый модуль, написанный вами или кем-то еще). Не ставьте эти проверки во все ваши промежуточные функции, так как такие проверки будут повторяться и действительно необоснованны.
ИЗМЕНИТЬ
Чтобы устранить проблему ошибок в промежуточных функциях, поднятых @Nasser в комментариях: существует очень простая методика, позволяющая включать и выключать проверки шаблонов одним щелчком мыши. Вы можете хранить ваши шаблоны в переменных внутри вашего пакета, определенных перед определениями ваших функций.
Вот пример, где f
- функция верхнего уровня, а g
и h
- "внутренние функции". Мы определяем два шаблона: для основной и для внутренних, так:
Clear[nlPatt,innerNLPatt ];
nlPatt= _?(!VectorQ[#,NumericQ]&);
innerNLPatt = nlPatt;
Теперь мы определяем наши функции:
ClearAll[f,g,h];
f[vector:nlPatt]:=g[vector]+h[vector];
g[nv:innerNLPatt ]:=nv^2;
h[nv:innerNLPatt ]:=nv^3;
Обратите внимание, что шаблоны заменяются внутри определений во время определения, а не во время выполнения, поэтому это в точности эквивалентно кодированию этих шаблонов вручную. После тестирования вы просто должны изменить одну строку: от
innerNLPatt = nlPatt
to
innerNLPatt = _
и перезагрузите пакет.
Последний вопрос: как вы быстро находите ошибки? Я ответил, что здесь, в разделах "Вместо возврата $Failed
можно сделать исключение, используя Throw." И "Meta-programming and automation".
END EDIT
Я включил краткое обсуждение этой проблемы в моей книге здесь. В этом примере показатель производительности был на уровне 10% увеличения времени выполнения, что ИМО является приемлемым на границе. В данном случае проверка проще, а штраф за производительность намного меньше. Как правило, для функции, которая является любой вычислительно-интенсивной, корректно написанные проверки типов стоят лишь малую долю от общего времени выполнения.
Несколько трюков, которые хорошо знать:
- Шаблон-сопряжение может быть очень быстрым, когда синтаксически используется (нет
Condition
илиPatternTest
).
Например:
randomString[]:[email protected][{97,122},5];
rstest = Table[randomString[],{1000000}];
In[102]:= MatchQ[rstest,{__String}]//Timing
Out[102]= {0.047,True}
In[103]:= MatchQ[rstest,{__?StringQ}]//Timing
Out[103]= {0.234,True}
Просто потому, что в последнем случае использовался PatternTest
, проверка выполняется намного медленнее, потому что оценщик вызывается шаблоном-сопоставлением для каждого элемента, в то время как в первом случае все чисто синтаксическое и все делается внутри совпадение шаблонов.
- То же самое верно для распакованных числовых списков (разница во времени аналогична). Однако для упакованных числовых списков
MatchQ
и другие функции тестирования шаблонов не распаковываются для определенных специальных шаблонов, более того, для некоторых из них проверка выполняется мгновенно.
Вот пример:
In[113]:=
test = RandomInteger[100000,1000000];
In[114]:= MatchQ[test,{__?IntegerQ}]//Timing
Out[114]= {0.203,True}
In[115]:= MatchQ[test,{__Integer}]//Timing
Out[115]= {0.,True}
In[116]:= Do[MatchQ[test,{__Integer}],{1000}]//Timing
Out[116]= {0.,Null}
То же самое, по-видимому, справедливо для таких функций, как VectorQ
, MatrixQ
и ArrayQ
с определенными предикатами (NumericQ
) - эти тесты чрезвычайно эффективны.
- Многое зависит от того, как вы пишете тест, то есть в какой степени вы повторно используете эффективные структуры Mathematica.
Например, мы хотим проверить, что у нас есть действительная числовая матрица:
In[143]:= rm = RandomInteger[10000,{1500,1500}];
Вот самый простой и медленный способ:
In[144]:= MatrixQ[rm,NumericQ[#]&&Im[#]==0&]//Timing
Out[144]= {4.125,True}
Это лучше, так как мы лучше используем шаблон-сопоставление:
In[145]:= MatrixQ[rm,NumericQ]&&FreeQ[rm,Complex]//Timing
Out[145]= {0.204,True}
Однако мы не использовали упакованный характер матрицы. Это еще лучше:
In[146]:= MatrixQ[rm,NumericQ]&&Total[Abs[Flatten[Im[rm]]]]==0//Timing
Out[146]= {0.047,True}
Однако это еще не конец. Следующий момент близок к мгновенному:
In[147]:= MatrixQ[rm,NumericQ]&&Re[rm]==rm//Timing
Out[147]= {0.,True}