List Haskell:使用整数筛选列表

List Haskell:使用整数筛选列表,list,haskell,filter,List,Haskell,Filter,如何过滤列表,以便只返回整数列表 例如,过滤像[1,1.2,2,2.2]这样的列表将返回[1,2]首先,您的列表应该是同质的,所以不能有整数和双倍的列表 有一个很好的函数properFraction,它将数字分解为整数和小数部分: properFraction :: (Fractional a, Integral b) => a -> (b,a) 所以,我们可以定义一个函数来计算数字是否有非零的小数部分 > let haveNoFractionalPart = (== 0.0

如何过滤列表,以便只返回整数列表


例如,过滤像
[1,1.2,2,2.2]
这样的列表将返回
[1,2]
首先,您的列表应该是同质的,所以不能有
整数和
双倍的列表

有一个很好的函数
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)
无论如何,很难想象需要从您拥有的一些值列表中过滤您所称的
整数的情况。没有这样的方法来定义任何带有浮点数的数字都有100%概率的零浮点数


也许在
Integer
Double
上使用一些包装器会有所帮助。

考虑到您的列表属于
[Double]
类型,因为您不能(以任何简单的方式)拥有包含不同类型元素的列表

一旦你有了一张双人床的清单,你就可以使用功能
天花

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 d = (fromIntegral $ ceiling d :: Rational) == d
然后,它似乎也适用于大分数

> nonFractional  (12345678987654321.5)
True
那么这个呢:

filterInt :: (RealFrac a) => [a] -> [Integer]
filterInt [] = []
filterInt (x:xs)
    | frac == 0 = a : filterInt xs
    | otherwise = filterInt xs
  where
      (a, frac) = properFraction x
测试:


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

hasFraction' :: Rational -> Bool
hasFraction' = (/= 1) . denominator
这可以推广到任何
Real
,是检查数字是否有小数部分的最安全方法之一:

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

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

这取决于数据的来源

Haskell不允许混合纯整数和非整数, 因此,除非使用更精确的数据类型,如
Rational
,否则整数将被
Double
等数据类型固有的不精确性所污染, 但考虑到您无论如何都不需要非整数,如果可以的话,在它们成为数字数据之前,将它们从源代码中丢弃

  • 如果您从用户处获得数据,请使用仅允许用户输入数字序列的输入表单,或使用下面的
    getInt
  • 如果数据来自数据库或其他基于文本的源,请使用下面的
    getInt
  • 如果您从一些不受控制的代码(库或外部调用)获取数据,是否有其他方法只提供整数?
    如果是,请使用它,如果不是,请在其他答案中使用其他基于舍入的解决方案之一
getInt
将字符串转换为整数,巧妙地忽略任何非整数的内容:

import Data.Char (isDigit)

getInt :: String -> Maybe Integer
getInt xs | all isDigit xs = Just (read xs)
          | otherwise      = Nothing
因此
getInt“12345”
只是
12345
,而
getInt 12345678987654321.1
。 我们可以使用它从一些列表中删除非整数输入:

getInts :: [String] -> [Integer]
getInts xss = catMaybes $ map getInt xss
或者更仔细地说,我们可以写
getInts=catMaybes.map getInt

现在
catMaybes::[maybea a]->[a]
它去掉了
Nothing
s,只打开
s。我们需要
导入数据。可能(catMaybes)
在顶部获取数据

如果数据是某种浮点数, 请记住,浮点类型中没有真正的相等, 因此,即使在检查之前转换为更精确的表示, 从逻辑上讲,你不可能知道原始数据 表示一个精确的整数或仅表示一个非常接近整数的值 在数据到达之前,浮点表示已四舍五入。 例如:

Prelude> (12345678987654321.6 :: Double) == 12345678987654322.0
True
isInt :: (RealFrac a) => a -> Bool
isInt x = x == (fromIntegral $ round x)
鉴于

Prelude> (12345678987654321.6 :: Rational) == 12345678987654322.0
False
但是如果您可以选择数据类型,那么您就可以控制生成代码,所以请选择不包含非整数

小结:在将非整数转换为数字数据之前,最容易去除它们,
而且你也不会偶尔出现奇怪的舍入错误

您的列表必须是
[Double]
[Integer]
类型,或其他类型的数字。你不能混合类型

也就是说,如果您有一个双精度列表,并且您试图过滤掉那些不是整数的,那么您可以始终使用
舍入
下限
、或
上限
来检查与数字的等效性

例如:

Prelude> (12345678987654321.6 :: Double) == 12345678987654322.0
True
isInt :: (RealFrac a) => a -> Bool
isInt x = x == (fromIntegral $ round x)
然后,您可以使用以下方法筛选数据:
filter

filter isInt [1, 1.2, 2, 2.2] -- [1.0, 2.0]

此列表中没有
整数
。一个列表只有相同类型的元素,所以这里如果有double,那么所有的元素都是double。为什么需要这个?如果您提供一些上下文,我们可以给出更适合您的问题的答案。如果您想忽略某些用户输入中的非整数,最好在数据仍然是字符串的情况下解决此问题。请注意,一般来说,无法测试浮点数是否相等,因为表示不准确,因此只能测试近似相等,并且必须确定接近程度是否足够。这就是其中一个答案使用
分数类的原因-它们存储为精确表示,因此可以测试它们是否相等以及它们是否为整数。
Prelude>properFraction(12345678987654321.5)
给出
(12345678987654322,0.0)
。properFraction也是如此。那么
(from integral$天花d::Rational)==d