Я опаздываю на вечеринку, с принятым ответом и всем, но я хочу отметить, что определения формы:
f[...] := Module[... /; ...]
очень полезны в этом контексте. Определения такого рода могут выполнять сложные вычисления, прежде чем, наконец, выручить и решить, что определение в конце концов не применимо.
Я проиллюстрирую, как это можно использовать для реализации различных стратегий обработки ошибок в контексте конкретного случая из другого вопроса SO . Проблема заключается в поиске фиксированного списка пар:
data = {{0, 1}, {1, 2}, {2, 4}, {3, 8}, {4, 15}, {5, 29}, {6, 50}, {7,
88}, {8, 130}, {9, 157}, {10, 180}, {11, 191}, {12, 196}, {13,
199}, {14, 200}};
, чтобы найти первую пару, чей второй компонент больше или равен указанному значению. Как только эта пара найдена, ее первый компонент должен быть возвращен. Есть много способов написать это в Mathematica, но вот один из них:
f0[x_] := First @ Cases[data, {t_, p_} /; p >= x :> t, {1}, 1]
f0[100] (* returns 8 *)
Вопрос теперь в том, что произойдет, если функция вызывается со значением, которое не может быть найдено?
f0[1000]
error: First::first: {} has a length of zero and no first element.
Сообщение об ошибке является в лучшем случае загадочным и не дает никаких подсказок о том, в чем заключается проблема. Если эта функция была вызвана глубоко в цепочке вызовов, то, скорее всего, произойдет каскад непрозрачных ошибок.
Существуют различные стратегии для решения таких исключительных случаев. Одним из них является изменение возвращаемого значения, чтобы случай успеха можно было отличить от случая отказа:
f1[x_] := Cases[data, {t_, p_} /; p >= x :> t, {1}, 1]
f1[100] (* returns {8} *)
f1[1000] (* returns {} *)
Тем не менее, существует строгая традиция Mathematica - оставлять исходное выражение неизменным всякий раз, когда функция оценивается с помощью аргументов за пределами своей области. Здесь модуль [... /; ...] шаблон может помочь:
f2[x_] :=
Module[{m},
m = Cases[data, {t_, p_} /; p >= x :> t, {1}, 1];
First[m] /; m =!= {}
]
f2[100] (* returns 8 *)
f2[1000] (* returns f2[1000] *)
Обратите внимание, что f2 полностью выручает, если конечный результат - пустой список, а исходное выражение возвращается без оценки - что достигается простым способом добавления /; условие до конечного выражения.
Можно принять решение о значительном предупреждении, если произойдет «не найденный» случай:
f2[x_] := Null /; Message[f2::err, x]
f2::err = "Could not find a value for ``.";
С этим изменением будут возвращены те же значения, но в случае «не найдено» будет выдано предупреждающее сообщение. Возвращаемое значение Null в новом определении может быть любым - оно не используется.
Можно также решить, что «не найденный» случай просто не может возникнуть вообще, за исключением случая с ошибочным клиентским кодом. В этом случае следует прервать вычисление:
f2[x_] := (Message[f2::err, x]; Abort[])
В заключение, эти шаблоны достаточно просты в применении, чтобы можно было иметь дело с аргументами функций, которые находятся за пределами определенной области. При определении функций стоит потратить несколько минут, чтобы решить, как обрабатывать ошибки домена. Это платит за сокращенное время отладки. В конце концов, фактически все функции являются частичными функциями в Mathematica. Подумайте: функцию можно вызывать с помощью строки, изображения, песни или роя наноботов (возможно, в Mathematica 9).
Последнее предостережение ... Я должен отметить, что при определении и переопределении функций с использованием нескольких определений очень легко получить неожиданные результаты из-за "оставшихся" определений. В качестве общего принципа я настоятельно рекомендую предопределять многократно определенные функции Clear :
Clear[f]
f[x_] := ...
f[x_] := Module[... /; ...]
f[x_] := ... /; ...