3

私は持っている方法を探しています(列挙型をとに簡単Enum a => UArray aにマッピングできるので、私には理にかなっています)InttoEnumfromEnum

これまでのところ、 Data.Array.BaseUArray Intからのコードを盗み、あちこちでいくつかのsとsを密輸しようとしました。toEnumfromEnum

{-# 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#ませんGHC.*。パターンマッチングI#は生成されませんIntが、Int#代わりに、何らかの形でUArray Int存在し、Int#sで機能します。

ニュータイプ用の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#)

I#...コンストラクターを省略できます。同様に、 を使用toEnumする場合は、コンストラクターを適用してI#、ボックス化されていない整数からボックス化された整数を取得する必要があります。

@leftaroundabout が述べたように、このボックス化とボックス化解除に加えて、複雑さfromEnum(toEnum特にタプルなど) に加えて、単純なボックス化された s を使用する場合と比較してパフォーマンスが低下する可能性がありArrayます。

于 2012-01-20T13:30:32.797 に答える
2

警告:関数fromEnum . toEnumは常に全単射であるとは限らないため、これはすべての列挙型で機能するとは限りません。特に、DoubleEnumインスタンスですが、値toEnumを切り捨てるだけDoubleです。

この理由Enumは、 のような式を書きたい場合に実装する必要がある型クラスだからです[0, 0.1 .. 1]。しかし、一般的に言えば、あなたがしていることは、いくつかのタイプではうまく機能しません。

于 2012-01-20T17:36:43.243 に答える