2012-01-20 3 views
3

私たちは自明toEnumfromEnumバックIntに列挙型をマッピングすることができて私には理にかなっている(Enum a => UArray aを持っている方法を探しています«インスタンス(列挙型aは、有界)=> IArray UArray»

{-# 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#,() #) 

しかし、もちろん、それはコンパイルされません:

は、これまでのところ私はtoEnum SとあちこちでfromEnum秒数をData.Array.BaseからUArray Intのコードを盗み、密輸しようとしました

[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. 

GHC.*には魔法unboxInt :: Int -> Int#が存在しない、とI#上のパターンマッチングはIntを生じないが、代わりにInt#、まだ何とかUArray Intが存在し、Int# Sに作用します。

また、making an UArray for newtypesについて投稿を見つけましたが、unsafeCoerceに依存しているため適用されていないようです。私はそれを試みたが、それはすべてのコンストラクタがBlueだった面白いlistArray (0, 54) $ cycle [Red, Yellow, Green]を作った。

間違っていますか?

更新:

それが機能するようになりました、ここではソースコードは次のとおりです。

+0

私はこれはとても良いアイデアだとは思わない。 'fromEnum'と' toEnum'はすべてのインスタンスでは必要ではありません。そのため、いくつかの型ではボックス化された配列よりも遅くなることがあります。 – leftaroundabout

+0

@leftaround私の(愚かで人工的な)ケースでは、 'data Color = Red | 100000の値の配列を格納して処理するために、時間と空間の両方の要件を70%削減しました。イエロー|グリーン|ブルー ' –

答えて

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に述べたように

、(特に、タプルのため、など)fromEnumtoEnumを有することができることを複雑に加えて、このボックス化とアンボックス化は、単純な箱入りArray Sを使用する場合に比べ少ない性能につながる可能性があります。

+0

ありがとう!どうにか私はunboxingが( 'case'と' I# 'とのマッチングを使って)どのように実行されたのか理解できませんでしたが、あなたの答えは物事をクリアしました。 –

2

警告:関数fromEnum . toEnumは必ずしも帰属とは限りません。したがって、すべての列挙型では機能しません。特に、DoubleEnumインスタンスですが、toEnumはちょうどDoubleの値を切り捨てます。

[0, 0.1 .. 1]のような式を書きたい場合は、Enumを実装する必要があるためです。しかし、一般的に言えば、あなたがやっていることは、いくつかのタイプではうまく動作しないということだけです。