Haskell:过滤列表与那些是整数

时间:2012-09-25 05:53:57

标签: list haskell filter

如何过滤列表以便仅返回整数列表?

例如,过滤[1, 1.2, 2, 2.2]之类的列表会返回[1, 2]

6 个答案:

答案 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的定义更改为

,请使用@ David的提示
nonFractional d = (fromIntegral $ ceiling d :: Rational) == d

然后它似乎适用于大分数

> nonFractional  (12345678987654321.5)
True

答案 1 :(得分:5)

首先,您的列表应该是同质的,因此您不能拥有IntegerDoubles的列表。

有一个很好的函数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%。

也许IntegerDouble上的某个包装器会有所帮助。

答案 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 12345getInt 12345678987654321.1Nothing。 我们可以使用它从某些列表中删除非整数输入:

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]类型,或其他类型的数字。你不能混合类型。

也就是说,如果你有一个双打列表并且你试图过滤掉那些不是整数的列表,你可以随时使用roundfloorceiling来检查数字的等效性。

例如:

isInt :: (RealFrac a) => a -> Bool
isInt x = x == (fromIntegral $ round x)

然后,您只需使用filter

过滤您的数据
filter isInt [1, 1.2, 2, 2.2] -- [1.0, 2.0]