我将如何过滤列表以便只返回整数列表?
例如,过滤一个类似的列表[1, 1.2, 2, 2.2]
会返回[1, 2]
.
考虑到您的列表是类型[Double]
,因为您不能(以任何简单的方式)包含不同类型元素的列表。
一旦你有一个 double 列表,你就可以使用函数ceiling
.
ceiling 2.1 = 3
ceiling 2.0 = 2
所以检查一个数字是否没有小数部分的函数可以写成
nonFractional d = (fromIntegral $ ceiling d) == d
现在您可以对此进行过滤
> filter nonFractional [1, 1.2, 2, 2.2]
[1.0,2.0]
(编辑)上述比较相等性的方法不适用于大数,例如
> nonFractional (12345678987654321.5)
True
nonFractional
如果您更改as的定义,请使用@David 的想法
nonFractional d = (fromIntegral $ ceiling d :: Rational) == d
然后它似乎也适用于大部分
> nonFractional (12345678987654321.5)
True
首先,您的列表应该是同质的,因此您不能拥有Integer
s 和 的列表Doubles
。
有一个很好的函数properFraction
,它将一个数字分解成整数部分和小数部分:
properFraction :: (Fractional a, Integral b) => a -> (b,a)
因此,我们可以定义一个函数来确定数字是否具有非零小数部分。
> let haveNoFractionalPart = (== 0.0) . snd . properFraction
haveNoFractionalPart :: Double -> Bool
不,我们可以使用该功能过滤您的列表:
> filter haveNoFractionalPart [1, 1.2, 2, 2.2]
[1.0,2.0]
更新:
我应该承认,我的解决方案在现实世界中的某些情况下无效且不可行。因为类似的东西
> properFraction (11111111111111111111.1)
(11111111111111110656,0.0)
无论如何,很难想象需要Integer
从您拥有的某些值列表中过滤您调用的内容的情况。并且没有这样的方法可以定义任何具有浮点数的数字都以 100% 的概率具有零浮动部分。
也许一些包装纸Integer
会Double
有所帮助。
已经发布了许多解决方案Rational
,实际上您只需要将分母与 1 进行比较:
hasFraction' :: Rational -> Bool
hasFraction' = (/= 1) . denominator
这可以推广到任何情况Real
,并且是检查数字是否有小数部分的最安全方法之一:
hasFraction :: (Real a) => a -> Bool
hasFraction = hasFraction' . toRational
该函数不能解决舍入误差问题,但这是很自然的。当舍入错误困扰您时,您使用了错误的数据类型。
那这个呢:
filterInt :: (RealFrac a) => [a] -> [Integer]
filterInt [] = []
filterInt (x:xs)
| frac == 0 = a : filterInt xs
| otherwise = filterInt xs
where
(a, frac) = properFraction x
测试:
> let li = [1, 1.2, 2, 2.2]
> filterInt li
> [1,2]
这取决于您从哪里获得数据。
Haskell 不允许您将纯整数与非整数混合,因此您的整数将受到数据类型固有的不准确性的影响,Double
除非您使用更准确的数据类型Rational
,但鉴于您不想要非整数,如果可以的话,在它们成为数字数据之前将它们从源头扔掉。
getInt
以下表单。getInt
在下面使用。getInt
将 String 转换为 Integer,巧妙地忽略任何不是 Integer 的内容:
import Data.Char (isDigit)
getInt :: String -> Maybe Integer
getInt xs | all isDigit xs = Just (read xs)
| otherwise = Nothing
getInt "12345"
也是,Just 12345
而是。_ 我们可以使用它从某个列表中删除非整数输入:getInt 12345678987654321.1
Nothing
getInts :: [String] -> [Integer]
getInts xss = catMaybes $ map getInt xss
或者更简洁地说,我们可以写
getInts = catMaybes.map getInt
.
现在catMaybes :: [Maybe a] -> [a]
,它摆脱了Nothing
s 并展开了Just
s。我们需要
import Data.Maybe (catMaybes)
在顶部才能得到它。
如果您的数据以某种浮点数的形式出现,请记住浮点类型中没有真正的相等性,因此即使您在检查之前转换为更准确的表示形式,您在逻辑上也不可能知道原始data 表示一个精确的整数,或者只是一个非常接近于浮点表示在数据到达您之前四舍五入的整数的东西。例如:
Prelude> (12345678987654321.6 :: Double) == 12345678987654322.0
True
然而
Prelude> (12345678987654321.6 :: Rational) == 12345678987654322.0
False
但是,如果您可以选择数据类型,则您可以控制生成代码,因此请选择不包含非整数!
总结:在将非整数转换为数值数据之前,最容易摆脱它们,并且不会偶尔出现奇怪的舍入错误。
您的列表必须是[Double]
or类型[Integer]
,或其他类型的数字。您不能混合类型。
也就是说,如果你有一个双精度列表并且你试图过滤掉那些不是整数的,你总是可以使用round
, floor
, 或ceiling
检查数字的等价性。
例如:
isInt :: (RealFrac a) => a -> Bool
isInt x = x == (fromIntegral $ round x)
然后你可以使用这个过滤你的数据,使用filter
:
filter isInt [1, 1.2, 2, 2.2] -- [1.0, 2.0]