我试图在Haskell中使用泛型编程,需要对数字数组进行排序,但由于某种原因,当我运行代码时,我收到一个错误声明"在表达式中没有(分数Nums)的实例:645.41...."每次我看我的代码,我认为它是有意义的,但我不确定为什么它不工作…
import Data.List (sortBy)
import Data.Ord (comparing)
data Nums = Nums {numbers::Double} deriving(Ord, Eq, Show)
sortNums :: [Nums] -> [Nums]
sortNums = sortBy(comparing numbers)
arr = [645.41, 37.59, 76.41, 5.31, 1.11, 1.10, 23.46, 635.47, 467.83, 62.25]
main:: IO ()
main =
do
print(sortNums arr)
如果这段代码看起来很乱或者没有意义,我很抱歉,我是Haskell的新手....
除非你有一个很好的理由,从问题中不明显,你可能应该完全删除Nums
。
import Data.List (sort)
arr :: [Double]
arr = [645.41, 37.59, 76.41, 5.31, 1.11, 1.10, 23.46, 635.47, 467.83, 62.25]
main :: IO ()
main = print (sort arr)
这里的问题是arr
是Double
s的数组,但您在其上调用sortNums
,这需要Nums
的数组。虽然它们实际上是"相同的"。类型,Haskell不会为您做任何自动转换。如果你把print(sortNums arr)
替换为print (sortNums (map Nums arr))
——以确保你排序的列表的每个成员都有一个Nums
值——那么这就像预期的那样工作了。
(不幸的是,您在这里收到的错误消息相当令人困惑且毫无帮助,但它源于这样一个事实,即像645.41
这样的文字浮点值可以表示Fractional
类型类实例的任何类型的值。GHC看到你正试图将sortNums
应用到列表中,并且这需要Nums
的列表,因此它试图使Nums
类型列表中的值-它可以做到,但只有当有Fractional
实例时,这就是为什么你得到特定的错误消息。不要太担心这一点-我只是想解释为什么你会得到这个特定的错误,而不是一个更有帮助的错误,如"无法匹配类型[Double]
与预期类型[Nums]
">
要让字面量1
或3.1415
适用于您的类型,您需要它们分别是Num
和Fractional
的实例:
>> :type 1
1 :: Num p => p
>> :type 3.1415
3.1415 :: Fractional p => p
这意味着你可以将1
实例化为Int
和Double
,但是你不能将它实例化为Nums
。
>> 1 :: Int
1
>> 1 :: Double
1.0
>> 1 :: Nums
<interactive>:36:1: error:
• No instance for (Num Nums) arising from the literal ‘1’
• In the expression: 1 :: Nums
In an equation for ‘it’: it = 1 :: Nums
同样,你可以将3.1415
实例化为Double
,但不能实例化为Int
或Nums
,因为没有Fractional Int
实例,也没有Fractional Nums
实例。
>> 1.0 :: Int
<interactive>:37:1-3: error:
• No instance for (Fractional Int) arising from the literal ‘1.0’
• In the expression: 1.0 :: Int
In an equation for ‘it’: it = 1.0 :: Int
>> 1.0 :: Double
1.0
>> 1.0 :: Nums
<interactive>:39:1-3: error:
• No instance for (Fractional Nums) arising from the literal ‘1.0’
• In the expression: 1.0 :: Nums
In an equation for ‘it’: it = 1.0 :: Nums
但是,可以为Nums
派生Num
,Fractional
实例。
- 将其设置为
newtype
而不是data
类型。这使得它在运行时在表示上等于Double
。 - 这意味着您可以使用
GeneralizedNewtypeDeriving
(newtype
派生策略)为您的Nums
类型(实际上Double
具有的任何实例)派生Num
和Fractional
。 - 您也可以使用
sortOn cmp
=sortBy (comparing cmp)
。
{-# Language DerivingStrategies #-}
{-# Language GeneralizedNewtypeDeriving #-}
{-# Language TypeApplications #-}
import Data.List (sortOn)
newtype Nums = Nums { numbers :: Double }
deriving
stock (Eq, Ord, Show)
deriving
newtype (Num, Fractional, Floating, Enum, Real, RealFloat, RealFrac)
sortNums :: [Nums] -> [Nums]
sortNums = sortOn numbers
arr :: Fractional a => [a]
arr = [645.41, 37.59, 76.41, 5.31, 1.11, 1.10, 23.46, 635.47, 467.83, 62.25]
-- >> main
-- [Nums {numbers = 1.1},Nums {numbers = 1.11},Nums {numbers = 5.31},Nums {numbers = 23.46},Nums {numbers = 37.59},Nums {numbers = 62.25},Nums {numbers = 76.41},Nums {numbers = 467.83},Nums {numbers = 635.47},Nums {numbers = 645.41}]
main :: IO ()
main = print (sortNums arr)
实际上,我们正在实例化arr :: [Nums]
,这是可能的,因为我们为Nums
创建了newtype派生的Fractional
:
>> :set -XTypeApplications
>>
>> arr @Float
[645.41,37.59,76.41,5.31,1.11,1.1,23.46,635.47,467.83,62.25]
>> arr @Double
[645.41,37.59,76.41,5.31,1.11,1.1,23.46,635.47,467.83,62.25]
>> arr @Double
[Nums {numbers = 645.41},Nums {numbers = 37.59},Nums {numbers = 76.41},Nums {numbers = 5.31},Nums {numbers = 1.11},Nums {numbers = 1.1},Nums {numbers = 23.46},Nums {numbers = 635.47},Nums {numbers = 467.83},Nums {numbers = 62.25}]