В SML, почему вам не разрешена настоящая константа в шаблоне?

Этот код не принимается;

> fun fact 0.0 = 1.0
Error-Real constants not allowed in patterns
> | fact n = n*fact(n-1);
Static Errors

Почему это?


person Toni Gargaro    schedule 08.12.2016    source источник
comment
См. часть Сравнение reals этот ответ.   -  person Simon Shine    schedule 08.12.2016


Ответы (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 — целое число (даже если это происходит только из-за округления). ошибка выключения). Это именно та проблема, которую пытались предотвратить создатели SML. Гораздо разумнее определить fact для получения и возврата целых чисел:

fun fact x = if x = 0 then 1 else x * fact(x-1)

(или -- почитайте о гамма-функции, если вам действительно нужен факториал с плавающей запятой).

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

person John Coleman    schedule 08.12.2016
comment
Возможно, стоит отметить, что у вас также есть Real.==, если вы явно этого хотите. - person Andreas Rossberg; 08.12.2016
comment
@AndreasRossberg Спасибо, что указали на это. Мне никогда не приходилось им пользоваться, и я забыл, что он вообще существует. Я добавил это к ответу. - person John Coleman; 08.12.2016
comment
Я не уверен, как должно работать fun fact x = if x == 0.0 then ..., но если по какой-то причине ввод x округлен неправильно, эта функция выдаст Out_of_memory. Плавающие значения должны быть сопоставлены должным образом. :-) - person Simon Shine; 09.12.2016
comment
@SimonShine Я не говорил, что намерения хорошие, но если вы собираетесь написать функцию, которая будет вычислять факториал действительных чисел с 0 после запятой, то именно так вы и поступили бы. По сути, это то, что вы сделали бы с JavaScript, который не имеет отдельного типа int, а просто использует 64-битные числа с плавающей запятой для всех чисел. Тем не менее, я должен уточнить. - person John Coleman; 09.12.2016
comment
@JohnColeman: Спасибо, не знал о гамма-функции. - person Simon Shine; 09.12.2016
comment
По-видимому, существует пакет Math.Gamma для Haskell. . - person Simon Shine; 09.12.2016
comment
Я думаю, что главная причина того, что real больше не допускает равенство в SML '97, заключается в том, что IEEE 754, который определяет числа с плавающей запятой, определяет их равенство непоследовательным образом; например, если x равно NaN, то Real.==(x, x) ложно! (SML '90 не указывал, как будет вести себя = при задании NaN, и не предлагал отдельный примитив для равенства с плавающей запятой IEEE 754, поэтому меня не удивит, если некоторые реализации обрабатывают его одним способом, а некоторые реализации другой.) - person ruakh; 18.12.2016