在Haskell中对数组进行排序



我试图在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)

这里的问题是arrDoubles的数组,但您在其上调用sortNums,这需要Nums的数组。虽然它们实际上是"相同的"。类型,Haskell不会为您做任何自动转换。如果你把print(sortNums arr)替换为print (sortNums (map Nums arr))——以确保你排序的列表的每个成员都有一个Nums值——那么这就像预期的那样工作了。

(不幸的是,您在这里收到的错误消息相当令人困惑且毫无帮助,但它源于这样一个事实,即像645.41这样的文字浮点值可以表示Fractional类型类实例的任何类型的值。GHC看到你正试图将sortNums应用到列表中,并且这需要Nums的列表,因此它试图使Nums类型列表中的值-它可以做到,但只有当有Fractional实例时,这就是为什么你得到特定的错误消息。不要太担心这一点-我只是想解释为什么你会得到这个特定的错误,而不是一个更有帮助的错误,如"无法匹配类型[Double]与预期类型[Nums]">

要让字面量13.1415适用于您的类型,您需要它们分别是NumFractional的实例:

>> :type 1
1 :: Num p => p
>> :type 3.1415
3.1415 :: Fractional p => p

这意味着你可以将1实例化为IntDouble,但是你不能将它实例化为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,但不能实例化为IntNums,因为没有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实例。

  1. 将其设置为newtype而不是data类型。这使得它在运行时在表示上等于Double
  2. 这意味着您可以使用GeneralizedNewtypeDeriving(newtype派生策略)为您的Nums类型(实际上Double具有的任何实例)派生NumFractional
  3. 您也可以使用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}]

最新更新