簡體   English   中英

Haskell:篩選器列表,其中包含整數

[英]Haskell: Filter List with those that are Integers

我將如何過濾列表,以便僅返回整數列表?

例如,過濾類似[1, 1.2, 2, 2.2]將返回[1, 2]

考慮到列表的類型為[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 ,則使用nonFractional的想法

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

然后它似乎也適用於大部分

> nonFractional  (12345678987654321.5)
True

首先,您的列表應該是同質的,因此您不能擁有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進行一些包裝會有所幫助。

那這個呢:

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]

已經發布了許多針對Rational的解決方案,實際上,您實際上只需要將分母與1進行比較:

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

可以將其推廣到任何Real並且是檢查數字是否有小數部分的最安全方法之一:

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

該函數不能解決舍入誤差問題,但這很自然。 當四舍五入的錯誤困擾您時,您使用的是錯誤的數據類型。

這取決於您從何處獲取數據。

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 12345getInt 12345678987654321.1Nothing 我們可以使用它從某些列表中刪除非整數輸入:

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

或更簡而言之,我們可以寫
getInts = catMaybes.map getInt

現在catMaybes :: [Maybe a] -> [a] ,它擺脫了Nothing並解開了Just 我們需要
在頂部import Data.Maybe (catMaybes)以獲取它。

如果您的數據是某種浮點數,請記住,浮點類型沒有真正的相等性,因此,即使在檢查之前轉換為更准確的表示形式,從邏輯上講,您也不可能知道原始的數據表示一個精確的整數,或者非常接近浮點表示在數據到達之前四舍五入的整數。 例如:

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

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

但是,如果可以選擇數據類型,則可以控制生成代碼,因此請選擇不包括非整數!

簡介:在將非整數轉換為數值數據之前 ,最簡單的方法是將其消除,並且不會遇到偶發的四舍五入錯誤。

您的列表必須是[Double][Integer]類型,或其他一些數字。 您不能混合類型。

就是說,如果您有一個雙精度數列表,並且試圖過濾掉不是整數的雙精度數,則始終可以使用roundfloorceiling來檢查該數字是否相等。

例如:

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

然后,您可以使用filterfilter

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM