2013-10-02 19 views
1

我有一個編寫簡單函數時沒有太多重複自己的問題,下面是一個簡化示例。我試圖編寫的真正的程序是來自python的BI服務器的內存數據庫的端口。實際上有更多不同的類型(大約8個)和更多的邏輯,大部分可以表示爲對多態類型操作的函數,如向量a,但仍然有些邏輯必須處理不同類型的值。對基本類型的有限域的容器進行動態類型編寫

由於效率原因,單獨包裝每個值(使用[(Int,WrappedValue)]類型)不是一個選項 - 在實際代碼中,我使用的是非盒裝向量。

type Vector a = [(Int, a)] -- always sorted by fst 

data WrappedVector = -- in fact there are 8 of them 
     FloatVector (Vector Float) 
    | IntVector (Vector Int) 
    deriving (Eq, Show) 

query :: [WrappedVector] -> [WrappedVector] -- equal length 
query vectors = map (filterIndexW commonIndices) vectors 
    where 
     commonIndices = intersection [mapFstW vector | vector <- vectors] 

intersection :: [[Int]] -> [Int] 
intersection = head -- dummy impl. (intersection of sorted vectors) 

filterIndex :: Eq a => [Int] -> Vector a -> Vector a 
filterIndex indices vector = -- sample inefficient implementation 
    filter (\(idx, _) -> idx `elem` indices) vector 

mapFst :: Vector a -> [Int] 
mapFst = map fst 

-- idealy I whould stop here, but I must write repeat for all possible types 
-- and kinds of wrapped containers and function this: 

filterIndexW :: [Int] -> WrappedVector -> WrappedVector 
filterIndexW indices vw = case vw of 
    FloatVector v -> FloatVector $ filterIndex indices v 
    IntVector v -> IntVector $ filterIndex indices v 

mapFstW :: WrappedVector -> [Int] 
mapFstW vw = case vw of 
    FloatVector v -> map fst v 
    IntVector v -> map fst v 

-- sample usage of query 
main = putStrLn $ show $ query [FloatVector [(1, 12), (2, -2)], 
           IntVector [(2, 17), (3, -10)]] 

我怎樣才能表達不包裝和展開像mapFstW和filterIndexW功能,這樣的代碼?

回答

2

如果您願意使用一些編譯器擴展,ExistentialQuantification可以很好地解決您的問題。

{-# LANGUAGE ExistentialQuantification #-} 
{-# LANGUAGE StandaloneDeriving #-} 
module VectorTest where 

type PrimVector a = [(Int, a)] 

data Vector = forall a . Show a => Vector (PrimVector a) 

deriving instance Show Vector 

query :: [Vector] -> [Vector] -- equal length 
query vectors = map (filterIndex commonIndices) vectors 
    where 
     commonIndices = intersection [mapFst vector | vector <- vectors] 

intersection :: [[Int]] -> [Int] 
intersection = head -- dummy impl. (intersection of sorted vectors) 

filterIndex :: [Int] -> Vector -> Vector 
filterIndex indices (Vector vector) = -- sample inefficient implementation 
    Vector $ filter (\(idx, _) -> idx `elem` indices) vector 

mapFst :: Vector -> [Int] 
mapFst (Vector l) = map fst l 

-- sample usage of query 
main = putStrLn $ show $ query [Vector [(1, 12), (2, -2)], 
           Vector [(2, 17), (3, -10)]] 

如果您爲Vector編寫手動Show實例,則可以刪除StandaloneDeriving要求,例如,

instance Show Vector where 
    show (Vector v) = show v 
+0

這很好地概括了真正的問題(有一點Data.Typeable的幫助),謝謝! –

+0

很高興聽到這是有幫助的! –

1

用於包裝單一類型不影響性能的標準選項是做

{-# LANGUAGE GeneralizedNewtypeDeriving #-} -- so we can derive Num 
newtype MyInt = My Int deriving (Eq,Ord,Show,Num) 
newtype AType a = An a deriving (Show, Eq) 

,因爲它只在類型級別創建了一個差異 - 因爲這一切被編譯掉的數據表示是相同的。你甚至可以指定這些值是取消裝箱的,但是...這不會幫助你,因爲你正在包裝多種類型。

真正的問題是,您試圖用靜態類型語言來表示動態類型的解決方案。動態類型的性能必然會受到動態語言的影響,但在標記中卻是明確的。

有兩種解決方法:

  1. 接受的是動態類型包括了靜態類型的附加運行時檢查,並與醜陋的生活。
  2. 拒絕動態輸入的需要,接受多態輸入整理所有代碼並移動類型檢查以編譯時間和數據採集。

我覺得2是迄今爲止最好的解決方案,你應該放棄嘗試列出你想使用的所有類型的程序,而不是編程來使用任何類型。它整潔,清晰和高效。你檢查有效性並處理一次,然後停止擔心。

相關問題