В SML почему вы не можете использовать реальную константу в шаблоне?
Этот код не принят;
> fun fact 0.0 = 1.0
Error-Real constants not allowed in patterns
> | fact n = n*fact(n-1);
Static Errors
Почему это?
1 ответ
real
не является типом равенства. SML придает большое значение созданию корректно корректного кода. Сравнение двух действительных чисел на равенство чаще всего является плохой идеей, поскольку может случиться так, что x = y
математически, но из-за ошибки округления x != y
во время выполнения. Это печально известный источник ошибок в наивных реализациях численных алгоритмов. Поскольку это очень плохая идея, SML просто запрещает ее. Так как невозможно сопоставить вход для равенства с шаблоном 1.0
было бы бессмысленно разрешать это как шаблон.
В тех немногих случаях, когда вы действительно хотите сравнить два реала на равенство, вы можете использовать x <= y andalso x => y
, В качестве альтернативы (как указывает @AndreasRossberg), можно использовать стандартную библиотечную функцию Real.==
который используется как Real.==(x,y)
, Последнее выглядит немного странно, поэтому вы можете объявить его как инфиксный оператор:
val == = Real.==
infix 4 ==
а потом просто x == y
К сожалению, ни один из них не может быть превращен в шаблон, хотя они позволяют писать:
fun fact x = if x == 0.0 then 1.0 else x * fact(x-1.0)
который работает, как и предполагалось. С другой стороны, как указывает @SimonShine, произойдет сбой, если вы передадите ему любой ввод, который не имеет форму n.0
где n
это int (даже если это только из-за ошибки округления). Это именно та проблема, которую создатели SML пытались предотвратить. Это имеет гораздо больше смысла, чтобы определить fact
взять и вернуть целые:
fun fact x = if x = 0 then 1 else x * fact(x-1)
(или - читайте о гамма-функции, если вы действительно хотите факториал с плавающей точкой).
Последнее определение можно легко преобразовать в форму сопоставления с образцом, которую вы, похоже, пытались найти.