简体   繁体   English

enumFromTo如何工作?

[英]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 . 我认为你是在使用predsucc方法,它们返回了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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM