如何过滤列表以便仅返回整数列表?
例如,过滤[1, 1.2, 2, 2.2]
之类的列表会返回[1, 2]
。
答案 0 :(得分:6)
将您的列表视为[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
的定义更改为
nonFractional d = (fromIntegral $ ceiling d :: Rational) == d
然后它似乎适用于大分数
> nonFractional (12345678987654321.5)
True
答案 1 :(得分:5)
首先,您的列表应该是同质的,因此您不能拥有Integer
和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
上的某个包装器会有所帮助。
答案 2 :(得分:1)
这个怎么样:
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]
答案 3 :(得分:1)
已为Rational
发布了许多解决方案,实际上您实际上只需要将分母与1进行比较:
hasFraction' :: Rational -> Bool
hasFraction' = (/= 1) . denominator
这可以推广到任何Real
,并且是检查数字是否具有小数部分的最安全的方法之一:
hasFraction :: (Real a) => a -> Bool
hasFraction = hasFraction' . toRational
该函数不能解决舍入误差问题,但这很自然。当舍入错误困扰您时,您使用的是错误的数据类型。
答案 4 :(得分:1)
这取决于您从何处获取数据。
Haskell不允许你将纯整数与非整数混合,
所以你的整数会被Double
等数据类型固有的不准确性所污染,除非你使用像Rational
这样更精确的东西,
但是考虑到你不想要非整数,如果可以的话,在它们是数字数据之前将它们扔到源头。
getInt
。getInt
。 getInt
将String转换为整数,狡猾地忽略任何不是整数的东西:
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
并解开Just
s。我们需要
在顶部import Data.Maybe (catMaybes)
得到它。
如果您的数据是某种浮点数, 请记住,在浮点类型中没有真正的平等, 所以,即使您在检查之前转换为更准确的表示, 你在逻辑上不可能知道原始数据是否存在 表示一个精确的整数或只是一个非常接近整数的东西 浮点表示在数据到达之前舍入。 例如:
Prelude> (12345678987654321.6 :: Double) == 12345678987654322.0
True
,而
Prelude> (12345678987654321.6 :: Rational) == 12345678987654322.0
False
但是如果你可以选择数据类型,你就可以控制生成代码,所以选择不包含非整数!
总结:在将转换为数值数据之前,最简单的方法是删除非整数, 并且你不会偶尔遇到一些奇怪的回合错误。
答案 5 :(得分:0)
您的列表必须是[Double]
或[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]