0

我将如何过滤列表以便只返回整数列表?

例如,过滤一个类似的列表[1, 1.2, 2, 2.2]会返回[1, 2].

4

6 回答 6

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如果您更改as的定义,请使用@David 的想法

nonFractional d = (fromIntegral $ ceiling d :: Rational) == d

然后它似乎也适用于大部分

> nonFractional  (12345678987654321.5)
True
于 2012-09-25T06:47:27.813 回答
5

首先,您的列表应该是同质的,因此您不能拥有Integers 和 的列表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% 的概率具有零浮动部分。

也许一些包装纸IntegerDouble有所帮助。

于 2012-09-25T06:29:28.633 回答
1

已经发布了许多解决方案Rational,实际上您只需要将分母与 1 进行比较:

hasFraction' :: Rational -> Bool
hasFraction' = (/= 1) . denominator

这可以推广到任何情况Real,并且是检查数字是否有小数部分的最安全方法之一:

hasFraction :: (Real a) => a -> Bool
hasFraction = hasFraction' . toRational

该函数不能解决舍入误差问题,但这是很自然的。当舍入错误困扰您时,您使用了错误的数据类型。

于 2012-09-25T12:10:44.133 回答
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]
于 2012-09-25T11:11:50.373 回答
1

这取决于您从哪里获得数据。

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.1Nothing

getInts :: [String] -> [Integer]
getInts xss = catMaybes $ map getInt xss

或者更简洁地说,我们可以写
getInts = catMaybes.map getInt.

现在catMaybes :: [Maybe a] -> [a],它摆脱了Nothings 并展开了Justs。我们需要
import Data.Maybe (catMaybes)在顶部才能得到它。

如果您的数据以某种浮点数的形式出现,请记住浮点类型中没有真正的相等性,因此即使您在检查之前转换为更准确的表示形式,您在逻辑上也不可能知道原始data 表示一个精确的整数,或者只是一个非常接近于浮点表示在数据到达您之前四舍五入的整数的东西。例如:

Prelude> (12345678987654321.6 :: Double) == 12345678987654322.0
True

然而

Prelude> (12345678987654321.6 :: Rational) == 12345678987654322.0
False

但是,如果您可以选择数据类型,则您可以控制生成代码,因此请选择不包含非整数!

总结:在将非整数转换为数值数据之前,最容易摆脱它们,并且不会偶尔出现奇怪的舍入错误。

于 2012-09-25T20:17:19.110 回答
0

您的列表必须是[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]
于 2012-09-26T15:53:12.307 回答