读取 Haskell 中数字的通用函数
General function for reading numbers in Haskell
所以,我想从用户那里得到一个 Float
,我已经实现了这个功能:
getFloat :: IO Float
getFloat = do
string <- getLine
return (read string :: Float)
现在我想知道如何制作一个更通用的函数,可以 return 整数、双精度和浮点数。
我尝试使用 Num
类型 class 来容纳更多的可能性,但它不起作用。
据我所知,它可以编译,但我不确定我在这里到底在做什么,也不确定它是否有效。
etNumber :: (Read a) => IO a
getNumber = do
string <- getLine
return (read string)
写签名的时候
getNumber' :: Num a => IO a
这意味着此操作将需要能够提供调用者可能请求的任何结果类型 a
– 前提是该类型是 Num
class 的实例。所以本质上,动作对它必须产生的类型一无所知,尽管它可以使用特定 Num
实例的方法:
class Num a where
fromInteger :: Integer -> a
(+) :: a -> a -> a
...
请注意,这不会为您提供任何生成 fractional/floating-pt 的工具。数字,只有整数。你实际上可以写
getNumber' :: Num a => IO a
getNumber' = do
i <- readLn
return $ fromInteger i
但这非常无用,如果您实际上尝试将 0.3
之类的内容读取为浮点数,它确实会失败——因为无法通过中间 Integer
类型将其拉出。
你可以这样做:
getNumber'' :: Fractional a => IO a
getNumber'' = do
q <- readLn
return $ fromRational q
在这种情况下,输入将首先被读取为任意精度的有理数类型(可以处理小数输入),然后转换为所需的最终类型,如Double
1。但是,这不能不是整数类型,因为那些显然无法处理可能的小数输入!
也许你的设想是这样的:
getNumber''' :: IO (∃a. Num a => a)
这将是一个 存在类型 。这基本上是一个受约束的 动态类型 ,即类型不是通过从调用者想要的推断在编译类型中选择的,而是在运行时选择可以正确处理的合适类型特定的字符串输入(尽可能为整数,如果需要则为浮动)。
嗯,Haskell 没有2 存在类型,for good reasons。它的标准参数多态性更有用,因为您实际上可以保证某些类型正是您在编译时指定的类型。值应该是整数?使它成为 Integer
;如果在输入中出现小数部分,您就会正确地收到一条有意义的错误消息。价值可能是小数?设为 Rational
或 Double
;这些当然也包括整数值。
无论如何,没有真正的理由将操作限制为任何数字 class。如果你完全让它成为多态的,你应该只在实现所需的范围内限制它(即 Read
)。总结一下:直接用readLn
,根本不写任何getTʏᴘᴇ
动作
1作为一般规则,never use Float
除非您确定 Double
不是您想要的。
2好吧,它有一个(通常不推荐的)解决方法:存在限定的记录构造函数。
{-# LANGUAGE GADTs #-}
data SomeNum where
SomeNum :: Num a => a -> SomeNum
getNumber'''' :: IO SomeNum
所以,我想从用户那里得到一个 Float
,我已经实现了这个功能:
getFloat :: IO Float
getFloat = do
string <- getLine
return (read string :: Float)
现在我想知道如何制作一个更通用的函数,可以 return 整数、双精度和浮点数。
我尝试使用 Num
类型 class 来容纳更多的可能性,但它不起作用。
据我所知,它可以编译,但我不确定我在这里到底在做什么,也不确定它是否有效。
etNumber :: (Read a) => IO a
getNumber = do
string <- getLine
return (read string)
写签名的时候
getNumber' :: Num a => IO a
这意味着此操作将需要能够提供调用者可能请求的任何结果类型 a
– 前提是该类型是 Num
class 的实例。所以本质上,动作对它必须产生的类型一无所知,尽管它可以使用特定 Num
实例的方法:
class Num a where
fromInteger :: Integer -> a
(+) :: a -> a -> a
...
请注意,这不会为您提供任何生成 fractional/floating-pt 的工具。数字,只有整数。你实际上可以写
getNumber' :: Num a => IO a
getNumber' = do
i <- readLn
return $ fromInteger i
但这非常无用,如果您实际上尝试将 0.3
之类的内容读取为浮点数,它确实会失败——因为无法通过中间 Integer
类型将其拉出。
你可以这样做:
getNumber'' :: Fractional a => IO a
getNumber'' = do
q <- readLn
return $ fromRational q
在这种情况下,输入将首先被读取为任意精度的有理数类型(可以处理小数输入),然后转换为所需的最终类型,如Double
1。但是,这不能不是整数类型,因为那些显然无法处理可能的小数输入!
也许你的设想是这样的:
getNumber''' :: IO (∃a. Num a => a)
这将是一个 存在类型 。这基本上是一个受约束的 动态类型 ,即类型不是通过从调用者想要的推断在编译类型中选择的,而是在运行时选择可以正确处理的合适类型特定的字符串输入(尽可能为整数,如果需要则为浮动)。
嗯,Haskell 没有2 存在类型,for good reasons。它的标准参数多态性更有用,因为您实际上可以保证某些类型正是您在编译时指定的类型。值应该是整数?使它成为 Integer
;如果在输入中出现小数部分,您就会正确地收到一条有意义的错误消息。价值可能是小数?设为 Rational
或 Double
;这些当然也包括整数值。
无论如何,没有真正的理由将操作限制为任何数字 class。如果你完全让它成为多态的,你应该只在实现所需的范围内限制它(即 Read
)。总结一下:直接用readLn
,根本不写任何getTʏᴘᴇ
动作
1作为一般规则,never use Float
除非您确定 Double
不是您想要的。
2好吧,它有一个(通常不推荐的)解决方法:存在限定的记录构造函数。
{-# LANGUAGE GADTs #-}
data SomeNum where
SomeNum :: Num a => a -> SomeNum
getNumber'''' :: IO SomeNum