3

我正在寻找一种方法Enum a => UArray a(这对我来说很有意义,因为我们可以轻松地将枚举映射到andInt并返回)toEnumfromEnum

到目前为止,我试图UArray IntData.Array.Base窃取代码并在这里和那里走私一些toEnums 和s:fromEnum

{-# LANGUAGE MagicHash, UnboxedTuples #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}

module UArrays where

import           Data.Array.Base
import           Data.Array.ST
import           Data.Array.Unboxed

import           GHC.Base -- (Int(I#), Int#(..))
import           GHC.Prim -- (indexIntArray#, readIntArray#, writeIntArray#)
import           GHC.ST (ST(..), runST)

import           Unsafe.Coerce

instance (Enum a, Bounded a) => IArray UArray a where
    {-# INLINE bounds #-}
    bounds (UArray l u _ _) = (l, u)
    {-# INLINE numElements #-}
    numElements (UArray _ _ n _) = n
    {-# INLINE unsafeArray #-}
    unsafeArray lu ies = runST (unsafeArrayUArray lu ies minBound)
        {-# INLINE unsafeAt #-}
    unsafeAt (UArray _ _ _ arr#) (I# i#) =
        I# $ fromEnum (indexIntArray# arr# i#)
    {-# INLINE unsafeReplace #-}
    unsafeReplace arr ies = runST (unsafeReplaceUArray arr ies)
    {-# INLINE unsafeAccum #-}
    unsafeAccum f arr ies = runST (unsafeAccumUArray f arr ies)
    {-# INLINE unsafeAccumArray #-}
    unsafeAccumArray f initialValue lu ies =
      runST (unsafeAccumArrayUArray f initialValue lu ies)

-- data STUArray s i e = STUArray !i !i !Int (GHC.Prim.MutableByteArray# s)
instance (Enum a, Bounded a) => MArray (STUArray s) a (ST s) where
    {-# INLINE getBounds #-}
    getBounds (STUArray l u _ _) = return (l, u)
    {-# INLINE getNumElements #-}
    getNumElements (STUArray _ _ n _) = return n
    {-# INLINE unsafeNewArray_ #-}
    unsafeNewArray_ (l, u) = unsafeNewArraySTUArray_ (l, u) wORD_SCALE
    {-# INLINE newArray_ #-}
    newArray_ arrBounds = newArray arrBounds minBound
    {-# INLINE unsafeRead #-}
    -- unsafeRead :: GHC.Arr.Ix i => a i e -> Int -> m e
    unsafeRead (STUArray _ _ _ marr#) (I# i#) =
      ST $ \ s1# ->
      case readIntArray# marr# i# s1# of
        (# s2#, e# #) -> (# s2#, I# (toEnum e#) #)
    {-# INLINE unsafeWrite #-}
    -- unsafeWrite :: GHC.Arr.Ix i => a i e -> Int -> e -> m ()
    unsafeWrite (STUArray _ _ _ marr#) (I# i#) (I# e#) =
      ST $ \ s1# ->
      case writeIntArray# marr# (unsafeCoerce i#) (I# $ fromEnum e#) s1# of
        s2# -> (# s2#, () #)

但当然它不会编译:

[2 of 4] Compiling UArrays          ( UArrays.hs, interpreted )

UArrays.hs:27:14:
    Couldn't match expected type `Int#' with actual type `Int'
    In the return type of a call of `fromEnum'
    In the second argument of `($)', namely
      `fromEnum (indexIntArray# arr# i#)'
    In the expression: I# $ fromEnum (indexIntArray# arr# i#)

UArrays.hs:52:45:
    Couldn't match expected type `Int' with actual type `Int#'
    In the first argument of `toEnum', namely `e#'
    In the first argument of `I#', namely `(toEnum e#)'
    In the expression: I# (toEnum e#)

UArrays.hs:57:57:
    Couldn't match expected type `Int#' with actual type `Int'
    In the return type of a call of `fromEnum'
    In the second argument of `($)', namely `fromEnum e#'
    In the third argument of `writeIntArray#', namely
      `(I# $ fromEnum e#)'
Failed, modules loaded: Utils.

也没有魔法unboxInt :: Int -> Int#in GHC.*,并且模式匹配 onI#不会产生Int而是Int#相反,但以某种方式UArray Int存在并在Int#s 上起作用。

我还发现了一篇关于为 newtypes 制作 UArray 的帖子,但它似乎并不适用,因为它依赖于unsafeCoerce. 我试过了,但它使listArray (0, 54) $ cycle [Red, Yellow, Green]所有构造函数都变得有趣Blue

我在错误的轨道上吗?

更新:

它现在可以工作了,这里是源代码:

4

2 回答 2

3

您需要意识到这Int是一个装箱整数,它是Int#通过构造函数从未装箱的整数构造的I#。所以:

-- These functions aren't practical; they just demonstrate the types.
unboxInt :: Int -> Int#
unboxInt (I# unboxed) = unboxed

boxInt :: Int# -> Int
boxInt = I#

由于fromEnum已经返回一个装箱的整数,您不必通过再次应用来重新装箱I#,因此在此代码段中,例如:

{-I# $-} fromEnum (indexIntArray# arr# i#)

... you can simply leave out the I# constructor. Similarly, when using toEnum, you should apply the I# constructor to get a boxed integer out of an unboxed integer.

As @leftaroundabout mentioned, this boxing and unboxing in addition to the complexity that fromEnum and toEnum can have (Especially for tuples, etc) might lead to less performance compared to using simple boxed Arrays.

于 2012-01-20T13:30:32.797 回答
2

Warning: The function fromEnum . toEnum is not always a bijection, so this will not work for all enum types. In particular, Double is an Enum instance, but toEnum just truncates Double values.

这样做的原因Enum是,如果您想编写诸如[0, 0.1 .. 1]. 但一般来说,你正在做的事情对于某些类型根本不起作用。

于 2012-01-20T17:36:43.243 回答