首页 > 解决方案 > 在 GHC 中 enum 和 Int 之间转换的最快方法是什么?

问题描述

我想将枚举转换为 Int 或反之亦然,即实现 sum 类型的标记和 Int 之间的双向映射。我已经尝试过fromEnum,但它似乎不够快,然后我尝试了,unsafeCoerce但它没有按预期工作:

import Data.Time.Clock
import Data.Int
import Unsafe.Coerce
import Control.Monad (replicateM_)

data Color = R | G | B
    deriving (Enum)

main = do
    printT 1 $ (unsafeCoerce R :: Int8)
    printT 1000 $ (unsafeCoerce G :: Int8)
    printT 1000000 $ (unsafeCoerce B :: Int8)
    printT 1000000000 $ (unsafeCoerce R :: Int8)

    printT 1 $ (fromEnum R)
    printT 1000 $ (fromEnum G)
    printT 1000000 $ (fromEnum B)
    printT 1000000000 $ (fromEnum B)

---------- profile tools ------------

printT :: Show a => Int -> a -> IO ()
printT n x = print =<< timeIt n (pure x)

timeIt :: Int -> IO a -> IO a
timeIt n _ | n <= 0 = error "timeIt n | n <= 0"
timeIt n proc = do
    t0 <- getCurrentTime
    replicateM_ (n-1) proc
    x <- proc
    t1 <- getCurrentTime
    putStrLn ("-- Time Used (repeat " ++ show n ++ " times): " ++ show (t1 `diffUTCTime` t0))
    return x

那么最快的方法是什么?

标签: haskellghc

解决方案


Enum真的关心你的类型,还是其他人的类型?如果它是别人的,那么你不能保证你可以使用任何方法fromEnum,所以你不走运。如果它是您自己的类型,那么您可以使用 anewtype和模式同义词而不是派生来重新实现它,因此这fromEnum实际上是免费的(前提是编译器可以在您使用它的任何地方对其进行专门化):

{-# LANGUAGE PatternSynonyms #-}

module ColorEnum (Color(R,G,B)) where

import Data.Coerce (coerce)

newtype Color = UnsafeColor Int

pattern R, G, B :: Color
pattern R = UnsafeColor 0
pattern G = UnsafeColor 1
pattern B = UnsafeColor 2

maxColor :: Int
maxColor = 2

instance Enum Color where
  succ (UnsafeColor a)
    | a == maxColor = error "succ{Color}: tried to take `succ' of last tag in enumeration"
    | otherwise = UnsafeColor (a + 1)
  pred (UnsafeColor a)
    | a == 0 = error "pred{Color}: tried to take `pred' of first tag in enumeration"
    | otherwise = UnsafeColor (a - 1)
  toEnum a
    | a >= 0 && a <= maxColor = UnsafeColor a
    | otherwise = error $ "toEnum{Color}: tag (" ++ show a ++ ") is outside of enumeration's range (0," ++ show maxColor ++ ")"
  enumFrom (UnsafeColor a) = coerce [a..maxColor]
  enumFromThen (UnsafeColor a) (UnsafeColor b) = coerce [a,b..if a > b then 0 else maxColor]
  fromEnum = coerce

注意事项:

  • 这并不是对您如何进行基准测试的认可(事实上,正如评论者指出的那样,这可能是错误的)
  • fromEnum这种变化很有可能会使其他事情变慢
  • 所有这些代码只是为了替换data Color = R | G | B deriving (Enum)

推荐阅读