[英]How does enumFromTo work?
I cannot add a number to a Char
; 我不能给Char
添加一个数字; the following will fail to compile 'a' + 1
. 以下将无法编译'a' + 1
。 But yet, ['a'..'z']
successfully creates a string in which each of the character value is incremented. 但是, ['a'..'z']
成功创建了一个字符串,其中每个字符值都会递增。 Is there a special function that can increment a Char
? 是否有可以增加Char
的特殊功能?
I know that I can do chr (ord c + 1)
. 我知道我可以做chr (ord c + 1)
。
How does the ['a'..'z']
or the underlying enumFromTo
function increment the characters in the resulting String
? ['a'..'z']
或底层的enumFromTo
函数如何增加结果String
的字符?
Yes, there is a special function that can add to a Char
, from the same Enum
class that enumFromTo
is from, named succ
. 是的,有一个特殊的函数可以添加到Char
,来自enumFromTo
来自的同一个Enum
类,名为succ
。 Beware that it is partial: succ maxBound
is undefined, so take care to check the value of the character before you apply succ
. 请注意它是部分的: succ maxBound
是未定义的,因此在应用succ
之前请注意检查字符的值。 succ
is indeed the same as \\c -> chr (ord c + 1)
, as you can verify with the universe
package: 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
In fact the implementation of succ
in GHC is quite close to the function you suggested: 事实上,GHC中succ
的实现与你建议的功能非常接近:
instance Enum Char where
succ (C# c#)
| isTrue# (ord# c# /=# 0x10FFFF#) = C# (chr# (ord# c# +# 1#))
| otherwise = error ("Prelude.Enum.Char.succ: bad argument")
However, succ
is not used in the implementation of enumFromTo
in GHC: 但是,在GHC中实现enumFromTo
不使用succ
:
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
If you can squint past the nastiness that exists primarily for efficiency reasons, you can see that eftChar
is essentially using succ
, but an inlined version of it rather than an actual call to succ
(here, to avoid boxing and re-boxing the Char
being manipulated). 如果你可以eftChar
主要出于效率原因存在的肮脏,你可以看到eftChar
是使用succ
,而是内联版本而不是实际调用succ
(这里,为了避免拳击和重新拳击Char
是操作)。
I think you're after the pred
and succ
methods, which return the predecessor or successor of Enum a
. 我认为你是在使用pred
和succ
方法,它们返回了Enum a
的前身或继承者。 The problem is that for a Bounded
Enum
, if you apply succ
on the maximum member of the set you will get an error. 问题是对于有Bounded
Enum
,如果在集合的最大成员上应用succ
,则会出现错误。
Bearing this in mind, you can define enumFromTo
recursively as so (avoiding dangerous succ
calls): 记住这一点,你可以递归地定义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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.