enumFromTo 是如何工作的?
How does enumFromTo work?
我无法将数字添加到 Char
;以下将无法编译 'a' + 1
。但是,['a'..'z']
成功创建了一个字符串,其中每个字符值都递增。有没有可以增加Char
的特殊函数?
我知道我可以做到 chr (ord c + 1)
。
['a'..'z']
或基础 enumFromTo
函数如何增加结果 String
中的字符?
是的,有一个特殊的函数可以添加到 Char
,来自 Enum
class,enumFromTo
来自,名为 succ
.请注意它是部分的:succ maxBound
未定义,因此在应用 succ
之前请注意检查字符的值。 succ
确实和\c -> chr (ord c + 1)
是一样的,你可以用universe
包来验证:
> let avoidMaxBound f x = if x == maxBound then Nothing else Just (f x)
> avoidMaxBound succ == avoidMaxBound (\c -> chr (ord c + 1))
True
实际上 implementation of succ
in GHC 与您建议的函数非常接近:
instance Enum Char where
succ (C# c#)
| isTrue# (ord# c# /=# 0x10FFFF#) = C# (chr# (ord# c# +# 1#))
| otherwise = error ("Prelude.Enum.Char.succ: bad argument")
然而,succ
在GHC中enumFromTo
的实现中没有使用:
instance Enum Char where
{-# INLINE enumFromTo #-}
enumFromTo (C# x) (C# y) = eftChar (ord# x) (ord# y)
{-# RULES
"eftChar" [~1] forall x y. eftChar x y = build (\c n -> eftCharFB c n x y)
#-}
-- We can do better than for Ints because we don't
-- have hassles about arithmetic overflow at maxBound
{-# INLINE [0] eftCharFB #-}
eftCharFB :: (Char -> a -> a) -> a -> Int# -> Int# -> a
eftCharFB c n x0 y = go x0
where
go x | isTrue# (x ># y) = n
| otherwise = C# (chr# x) `c` go (x +# 1#)
{-# NOINLINE [1] eftChar #-}
eftChar :: Int# -> Int# -> String
eftChar x y | isTrue# (x ># y ) = []
| otherwise = C# (chr# x) : eftChar (x +# 1#) y
如果您能仔细观察主要出于效率原因而存在的麻烦,您会发现 eftChar
本质上是在使用 succ
,但它是一个内联版本,而不是对 [ 的实际调用=16=](此处,避免对 Char
进行装箱和重新装箱)。
我认为您在使用 pred
和 succ
方法,它们 return 是 Enum a
的前身或后继者。问题在于,对于 Bounded
Enum
,如果对集合的最大成员应用 succ
,则会出现错误。
考虑到这一点,您可以这样递归定义 enumFromTo
(避免危险的 succ
调用):
eftEnum :: (Enum a, Eq a, Ord a) => a -> a -> [a]
eftEnum a b
| a > b = []
| a == b = [a]
| otherwise = a : rest
where rest = eftEnum (succ a) b
我无法将数字添加到 Char
;以下将无法编译 'a' + 1
。但是,['a'..'z']
成功创建了一个字符串,其中每个字符值都递增。有没有可以增加Char
的特殊函数?
我知道我可以做到 chr (ord c + 1)
。
['a'..'z']
或基础 enumFromTo
函数如何增加结果 String
中的字符?
是的,有一个特殊的函数可以添加到 Char
,来自 Enum
class,enumFromTo
来自,名为 succ
.请注意它是部分的:succ maxBound
未定义,因此在应用 succ
之前请注意检查字符的值。 succ
确实和\c -> chr (ord c + 1)
是一样的,你可以用universe
包来验证:
> let avoidMaxBound f x = if x == maxBound then Nothing else Just (f x)
> avoidMaxBound succ == avoidMaxBound (\c -> chr (ord c + 1))
True
实际上 implementation of succ
in GHC 与您建议的函数非常接近:
instance Enum Char where
succ (C# c#)
| isTrue# (ord# c# /=# 0x10FFFF#) = C# (chr# (ord# c# +# 1#))
| otherwise = error ("Prelude.Enum.Char.succ: bad argument")
然而,succ
在GHC中enumFromTo
的实现中没有使用:
instance Enum Char where
{-# INLINE enumFromTo #-}
enumFromTo (C# x) (C# y) = eftChar (ord# x) (ord# y)
{-# RULES
"eftChar" [~1] forall x y. eftChar x y = build (\c n -> eftCharFB c n x y)
#-}
-- We can do better than for Ints because we don't
-- have hassles about arithmetic overflow at maxBound
{-# INLINE [0] eftCharFB #-}
eftCharFB :: (Char -> a -> a) -> a -> Int# -> Int# -> a
eftCharFB c n x0 y = go x0
where
go x | isTrue# (x ># y) = n
| otherwise = C# (chr# x) `c` go (x +# 1#)
{-# NOINLINE [1] eftChar #-}
eftChar :: Int# -> Int# -> String
eftChar x y | isTrue# (x ># y ) = []
| otherwise = C# (chr# x) : eftChar (x +# 1#) y
如果您能仔细观察主要出于效率原因而存在的麻烦,您会发现 eftChar
本质上是在使用 succ
,但它是一个内联版本,而不是对 [ 的实际调用=16=](此处,避免对 Char
进行装箱和重新装箱)。
我认为您在使用 pred
和 succ
方法,它们 return 是 Enum a
的前身或后继者。问题在于,对于 Bounded
Enum
,如果对集合的最大成员应用 succ
,则会出现错误。
考虑到这一点,您可以这样递归定义 enumFromTo
(避免危险的 succ
调用):
eftEnum :: (Enum a, Eq a, Ord a) => a -> a -> [a]
eftEnum a b
| a > b = []
| a == b = [a]
| otherwise = a : rest
where rest = eftEnum (succ a) b