如何有效地实例化类型类的 Eq



我做了以下类来在不同的温度类型之间进行转换:

data Temp = Kelvin Float | Celsius Float |Fahrenheit Float deriving Show
conversionKelvin:: Temp -> Temp
conversionKelvin (Celsius x) = Kelvin (x + 273.15)
conversionKelvin (Fahrenheit x) = Kelvin((x - 32) * 5/9 + 273.15)
conversionKelvin (Kelvin x) = Kelvin x
conversionCelsius:: Temp -> Temp
conversionCelsius (Kelvin x) = Celsius (x - 273.15)
conversionCelsius (Fahrenheit x) = Celsius((x - 32) * 5/9)
conversionCelsius (Celsius x) = Celsius x
conversionFahrenheit:: Temp -> Temp
conversionFahrenheit (Celsius x) = Fahrenheit (x * 9/5 + 32)
conversionFahrenheit (Kelvin x) = Fahrenheit((x - 273.15)*9/5 + 32)
conversionFahrenheit (Fahrenheit x) = Fahrenheit x

到目前为止一切都很好,但是我想实现实例 Eq 和 Ord。我想过将每种类型转换为摄氏度,然后看看哪个更大,但我无法通过编译器。 有什么帮助吗?

编辑:这是我尝试实例化 Eq:

instance Eq Temp where
a == b = conversionCelsius(a) == conversionCelsius(b)

它可以编译,但它使Haskell进入某种循环(不打印输出)

这会导致无限递归

instance Eq Temp where
a == b = conversionCelsius(a) == conversionCelsius(b)

这是因为比较a == b在两个Temp之间,而比较conversionCelsius(a) == conversionCelsius(b)又在两个Temp之间,所以我们永远递归。

要停止递归,您至少需要有一个基本情况,其中Temps 之间的比较是通过比较Floats 来完成的。一个可能的解决方案如下。

instance Eq Temp where
Celsius x == Celsius y = x == y
a         == b         = conversionCelsius a == conversionCelsius b

这将直接比较摄氏温度,比较它们的Float值。其他温度在递归之前转换为摄氏度。递归后,我们将立即找到基本情况,因此递归结束。

我认为您首先还需要将Kelvin转换为Kelvin,等等:

conversionKelvin:: Temp -> Temp
conversionKelvin (Celsius x) = Kelvin (x + 273.15)
conversionKelvin (Fahrenheit x) = Kelvin((x - 32) * 5/9 + 273.15)
conversionKelvink@(Kelvin _) = k

至于Eq实例,您确实可以首先将两个温度转换为相同的单位(无论该单位是什么),然后检查它们是否相等:

instance Eq Temp where
a == b | ~(Kelvinka) <- conversionKelvin a, ~(Kelvinkb) <- conversionKelvin b =ka == kb

话虽如此,使用Floats 进行的计算通常会导致舍入误差。因此,这意味着两个相同的Temp可以被认为是不同的,两个略有不同的Temp可以被认为是相同的。

你也可以实现一个函数toKelvin :: Temp -> Float,因为结果的单位很清楚:

toKelvin :: Temp ->Float
toKelvin (Celsius x) = x + 273.15
toKelvin (Fahrenheit x) = (x - 32) * 5/9 + 273.15
toKelvin(Kelvin k) = k

然后使用:

instance Eq Temp where
a == b =toKelvin a==toKelvin kb

如何为您的表示选择一个普通形式,派生Eq实例

type    Temp :: Type
newtype Temp = Celsius Float
deriving
newtype Eq

但随后为不同的单位创建模式同义词

pattern Kelvin :: Float -> Temp
pattern Kelvin kelvin <- Celsius (celsiusToKelvin -> kelvin)
where Kelvin kelvin =  Celsius (kelvinToCelsius kelvin)
pattern Fahrenheit :: Float -> Temp
pattern Fahrenheit fahrenheit <- Celsius (celsiusToFahrenheit -> fahrenheit)
where Fahrenheit fahrenheit =  Celsius (fahrenheitToCelsius fahrenheit)

最新更新