2017-02-21 8 views
3

ファンクションのタイプ定義を最も厳密にするか、最も緩いタイプにするのが一般的ですか?各アプローチの長所と短所は何ですか?私は、my pearson correlation codeを厳密に2倍書き換えてみると、書くこと、それに従うこと、そして理由をつけることがより簡単であることを発見しました(これはちょうど未熟なことかもしれません)。しかし、より広範な型定義を持つことが、関数をより一般的に適用可能にする方法を知ることもできます。より厳しい型定義は、技術的負債の一形態として特徴づけられるだろうか?ダブルスでファンクションタイプの制限

import Data.List 

mean :: Fractional a => [a] -> a 
mean xs = s/n 
    where 
     (s , n) = foldl' k (0,0) xs 
     k (s, n) x = s `seq` n `seq` (s + x, n + 1) 

covariance :: Fractional a => [a] -> [a] -> a 
covariance xs ys = mean productXY 
    where 
    productXY = zipWith (*) [x - mx | x <- xs] [y - my | y <- ys] 
    mx  = mean xs 
    my  = mean ys 

stddev :: Floating a => [a] -> a 
stddev xs = sqrt (covariance xs xs) 

pearson :: RealFloat a => [a] -> [a] -> a 
pearson x y = fifthRound $ covariance x y/(stddev x * stddev y) 

pearsonMatrix :: RealFloat a => [[a]] -> [[a]] 
pearsonMatrix (x:xs) = [pearson x y | y <- x:xs]:(pearsonMatrix xs) 
pearsonMatrix [] = [] 

fifthRound :: RealFrac a => a -> a 
fifthRound x = (/100000) $ fromIntegral $ round (x * 100000) 

:型クラスで

import Data.List 

mean :: [Double] -> Double 
mean xs = s/n 
    where 
     (s , n) = foldl' k (0,0) xs 
     k (s, n) x = s `seq` n `seq` (s + x, n + 1) 

covariance :: [Double] -> [Double] -> Double 
covariance xs ys = mean productXY 
    where 
    productXY = zipWith (*) [x - mx | x <- xs] [y - my | y <- ys] 
    mx  = mean xs 
    my  = mean ys 

stddev :: [Double] -> Double 
stddev xs = sqrt (covariance xs xs) 

pearson :: [Double] -> [Double] -> Double 
pearson x y = fifthRound (covariance x y/(stddev x * stddev y)) 

pearsonMatrix :: [[Double]] -> [[Double]] 
pearsonMatrix (x:xs) = [pearson x y | y <- x:xs]:(pearsonMatrix xs) 
pearsonMatrix [] = [] 

fifthRound :: Double -> Double 
fifthRound x = (/100000) $ fromIntegral $ round (x * 100000) 

答えて

8

可読性は意見の問題です。一般的に、より一般的な型シグネチャは、可能な定義が少ないために読みやすくなっています(場合によっては、1つの非分岐の定義しかないこともあります)。たとえば、meanにはFractionalという制約があるだけで、その機能で実行される操作はすぐに制限されます(これは、私が知っている限りsqrtの操作を実行できるDoubleのバージョンと比較して)。もちろん、一般化タイプis not alwaysmore readableです。 (And just for fun

機能のより一般的なバージョンを有することの主な欠点は、Floating機能のDoubleの辞書はmeanに、それが呼び出されるたびに渡さなければならないように、彼らは、実行時に最適化されていないままであるということです。

SPECIALIZE pragmaを追加すると、すべての世界の中で最高の結果を得ることができます。これは、基本的に、インスタンス化された型変数のいくつかと関数コードを複製するようにコンパイラに指示します。あなたはDoubleでほとんど唯一のあなたのmean関数を呼び出すことがしようとしている知っているなら、これは私が

{-# SPECIALIZE mean :: [Double] -> Double #-} 
mean :: Fractional a => [a] -> a 
mean xs = s/n 
    where 
    (s , n) = foldl' k (0,0) xs 
    k (s, n) x = s `seq` n `seq` (s + x, n + 1) 

を何をするのかで、あなたもあなたのコード内の署名の特殊なバージョンを見ることを得ます!わーい!

+1

時には、最も一般的な多形性のシグネチャは、あまりにも狂っています。制約リストだけが完全な特殊型よりも2倍長い場合は、これが本当に分かりやすいかどうか、それについて考えると思います。 ( 'ConstraintKind'-' type'defsはこのような署名をかなり読みやすくすることができますが、これはエラーメッセージなどではあいまいです。) – leftaroundabout