2017-02-21 51 views
3

Как правило, предпочтительнее иметь строгое или нечеткое определение типа для функции? Каковы плюсы и минусы каждого подхода? Я обнаружил, что, когда я переписал my pearson correlation code, используя строго удвоение, мне было легче писать, следовать и рассуждать (это может быть просто неопытность). Но я также вижу, как более широкое определение типа сделает функции более общеприменимыми. Можно ли охарактеризовать более строгие определения типов как форму технического долга?Ограничения типа функции

С классов типов:

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

читаемость это вопрос мнения. В общем, я нахожу, что более общие типы сигнатур более читабельны, потому что существует меньше возможных определений (иногда даже существует только одно неразрешимое определение). Например, вид, что mean имеет ограничение Fractional, немедленно ограничивает операции, выполняемые в этой функции (по сравнению с версией Double, которая может выполнять операции sqrt для всех, что я знаю). Конечно, обобщающие типы is not alwaysmore readable. (And just for fun)

Главный недостаток, имеющие более общие версии функций является то, что они могут оставаться неоптимизированными во время выполнения, так что словарь Double «s из Floating функций должны быть передан mean каждый раз, когда он вызывается.

Вы можете получить лучшее из всех миров, добавив SPECIALIZE pragma. Это говорит компилятору, чтобы в основном дублировать ваш функциональный код с некоторыми из созданных экземпляров типа. Если вы знаете, что вы собираетесь называть вашу mean функции в значительной степени только с Double, то это то, что я хотел бы сделать

{-# 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

Правильно, хотя иногда самая общая полиморфная подпись просто слишком сумасшедшая. Если список ограничений в два раза длиннее полного специализированного типа, я бы подумал об этом, действительно ли это разумно. (Хотя 'ConstraintKind'-'type'defs могут сделать такие подписи довольно читабельными, это происходит по цене безвестности в сообщениях об ошибках и т. Д.) – leftaroundabout

 Смежные вопросы

  • Нет связанных вопросов^_^