带分数的列表生成如何工作?
How does list generation with Fractionals work?
如果我想用输入生成一个列表:
[3.1,5.1..8.1]
GHC 8.6.3 returns:
[3.1,5.1,7.1,9.099999999999998]
我这里的问题不是9.1
的近似值,而是为什么GHC制作的列表比下面的解多了一个元素
在我在 GHC.Enum
中找到的文档中,enumFromThenTo
将其翻译成类似于以下内容:
-- | Used in Haskell's translation of @[n,n'..m]@ with
-- @[n,n'..m] = enumFromThenTo n n' m@, a possible implementation
-- being @enumFromThenTo n n' m = worker (f x) (c x) n m@,
-- @x = fromEnum n' - fromEnum n@, @c x = bool (>=) (<=) (x > 0)@
-- @f n y
-- | n > 0 = f (n - 1) (succ y)
-- | n < 0 = f (n + 1) (pred y)
-- | otherwise = y@ and
-- @worker s c v m
-- | c v m = v : worker s c (s v) m
-- | otherwise = []@
所以下面的代码:
import Data.Bool
eftt n s m = worker (f x) (c x) n m
where x = (fromEnum s) - (fromEnum n)
c x = bool (>=) (<=) (x > 0)
f n y
| n > 0 = f (n-1) (succ y)
| n < 0 = f (n+1) (pred y)
| otherwise = y
worker s c v m
| c v m = v: worker s c (s v) m
| otherwise = []
在与以前相同的输入上,但是 returns 这个列表:
[3.1,5.1,7.1]
GHC.Enum
中定义的实际实现如下:
enumFromThenTo x1 x2 y = map toEnum [fromEnum x1, fromEnum x2 .. fromEnum y]
但是GHC.Enum
中没有Enum Double
或Enum Float
的实例化
因此,当我尝试使用以下代码重现此内容时:
import Prelude(putStrLn,show)
import GHC.Enum(toEnum,fromEnum,Enum,enumFromThenTo)
import GHC.Base(map)
main = putStrLn (show (_enumFromThenTo 3.1 5.1 8.1))
_enumFromThenTo :: (Enum a) => a -> a -> a -> [a]
_enumFromThenTo x1 x2 y = map toEnum [fromEnum x1, fromEnum x2 .. fromEnum y]
我编译了:
$ ghc -package ghc -package base <file.hs>
结果又是:
[3.0,5.0,7.0]
这里发生了什么,输出变成:
[3.1,5.1,7.1,9.099999999999998]
?
Well, this is instance Enum Double
instance Enum Double where
enumFromThenTo = numericEnumThenFromTo
numericEnumFromThenTo :: (Ord a, Fractional a) => a -> a -> a -> [a]
numericEnumFromThenTo e1 e2 e3
= takeWhile predicate (numericEnumFromThen e1 e2)
where
mid = (e2 - e1) / 2
predicate | e2 >= e1 = (<= e3 + mid)
| otherwise = (>= e3 + mid)
比实现更重要的是上面的注释:
-- These 'numeric' enumerations come straight from the Report
指的是this passage in the (2010) Report:
For Float
and Double
, the semantics of the enumFrom
family is given by the rules for Int
above, except that the list terminates when the elements become greater than e3 + i∕2 for positive increment i, or when they become less than e3 + i∕2 for negative i.
(其中e3指上限,i指增量。)
您在 Enum
中找到的评论和 class Enum
中的实现均无关紧要。注释只是示例代码,详细说明了实例 可能 是如何实现的,并且给出的实现在 class 中,因此可以被任何东西覆盖。
如果我想用输入生成一个列表:
[3.1,5.1..8.1]
GHC 8.6.3 returns:
[3.1,5.1,7.1,9.099999999999998]
我这里的问题不是9.1
的近似值,而是为什么GHC制作的列表比下面的解多了一个元素
在我在 GHC.Enum
中找到的文档中,enumFromThenTo
将其翻译成类似于以下内容:
-- | Used in Haskell's translation of @[n,n'..m]@ with
-- @[n,n'..m] = enumFromThenTo n n' m@, a possible implementation
-- being @enumFromThenTo n n' m = worker (f x) (c x) n m@,
-- @x = fromEnum n' - fromEnum n@, @c x = bool (>=) (<=) (x > 0)@
-- @f n y
-- | n > 0 = f (n - 1) (succ y)
-- | n < 0 = f (n + 1) (pred y)
-- | otherwise = y@ and
-- @worker s c v m
-- | c v m = v : worker s c (s v) m
-- | otherwise = []@
所以下面的代码:
import Data.Bool
eftt n s m = worker (f x) (c x) n m
where x = (fromEnum s) - (fromEnum n)
c x = bool (>=) (<=) (x > 0)
f n y
| n > 0 = f (n-1) (succ y)
| n < 0 = f (n+1) (pred y)
| otherwise = y
worker s c v m
| c v m = v: worker s c (s v) m
| otherwise = []
在与以前相同的输入上,但是 returns 这个列表:
[3.1,5.1,7.1]
GHC.Enum
中定义的实际实现如下:
enumFromThenTo x1 x2 y = map toEnum [fromEnum x1, fromEnum x2 .. fromEnum y]
但是GHC.Enum
Enum Double
或Enum Float
的实例化
因此,当我尝试使用以下代码重现此内容时:
import Prelude(putStrLn,show)
import GHC.Enum(toEnum,fromEnum,Enum,enumFromThenTo)
import GHC.Base(map)
main = putStrLn (show (_enumFromThenTo 3.1 5.1 8.1))
_enumFromThenTo :: (Enum a) => a -> a -> a -> [a]
_enumFromThenTo x1 x2 y = map toEnum [fromEnum x1, fromEnum x2 .. fromEnum y]
我编译了:
$ ghc -package ghc -package base <file.hs>
结果又是:
[3.0,5.0,7.0]
这里发生了什么,输出变成:
[3.1,5.1,7.1,9.099999999999998]
?
Well, this is instance Enum Double
instance Enum Double where
enumFromThenTo = numericEnumThenFromTo
numericEnumFromThenTo :: (Ord a, Fractional a) => a -> a -> a -> [a]
numericEnumFromThenTo e1 e2 e3
= takeWhile predicate (numericEnumFromThen e1 e2)
where
mid = (e2 - e1) / 2
predicate | e2 >= e1 = (<= e3 + mid)
| otherwise = (>= e3 + mid)
比实现更重要的是上面的注释:
-- These 'numeric' enumerations come straight from the Report
指的是this passage in the (2010) Report:
For
Float
andDouble
, the semantics of theenumFrom
family is given by the rules forInt
above, except that the list terminates when the elements become greater than e3 + i∕2 for positive increment i, or when they become less than e3 + i∕2 for negative i.
(其中e3指上限,i指增量。)
您在 Enum
中找到的评论和 class Enum
中的实现均无关紧要。注释只是示例代码,详细说明了实例 可能 是如何实现的,并且给出的实现在 class 中,因此可以被任何东西覆盖。