Haskell:使用接口和多态函数实现设计

cib*_*en1 1 polymorphism haskell interface

我再次请求关于如何在Haskell中实现给定设计的评论.在此先感谢所有提供有用评论的人.此外,我希望这可以帮助像我这样的其他Haskell新手,有一个实用的示例代码.

这一次,我们有一个多态函数doSampling(在模块Samples中),它接受泛型函数f和一个实数(索引)列表并返回一个Samples(索引,值= f(索引)).我们doSampling只需要实现一次,因为如果f是a Polynomial或a 则无关紧要Sinus.为此,我们引入了一个接口函数,并使用Polynomial和Sinus类型实现它.以下是正在实施的设计:

在此输入图像描述

编辑1:

关于Function接口(Haskell中的类)存在争议.有人建议它实际上并不是必需的,因为doSampling可能会采用"裸体"功能(Double -> Double).但是,如何做到这一点,如果你需要的裸体函数内一些额外的状态多项式,功放+频率+相对于窦(coeffs?

编辑2:

kosmikus和Chris Taylor非常好的答案.谢谢.两者中的一个关键想法:有

doSampling :: (Double -> Double) -> [Double] -> Samples

这是:它需要一个函数(Double -> Double)(而不是Function)并列出并返回样本.

我的目的是保持Polynomials和Sinuses 的状态.在克里斯的答案中没有考虑到这一点,但它在kosmikus中.另一方面,Function如果您无法访问源代码,kosmikus版本中的弱点可能是如何扩展其定义.

我还要指出:

  • 克里斯的想法是(Double -> Double)通过工厂函数将多项式或正弦曲线封装到函数中,mkPolynomial或者mkSinus生成(使用currying?)获取适当参数的所需函数.(虽然你以后不能参考参数).

  • kosmikous'使用value转换(也使用currying?)a 的想法Functiona(Double -> Double)

  • 这两个答案都值得一读,因为他们有其他一些小的Haskell技巧来减少和简化代码.

总共

  • 克里斯的答案不支持保持多项式或窦的状态

  • kosmikus答案是不可扩展的:添加新类型的函数(Cosinus ......)

  • 我的答案(冗长)确实克服了以前的缺点,它允许(这不是问题所必需的)强制函数类型除了value(在java-interfaces如何工作的意义上)之外还有更多的关联函数.

我自己的方法

主要(用法)

import Polynomial
import Sinus
import Function
import Samples

-- ...............................................................
p1 = Polynomial [1, 0, 0.5]  -- p(x) =  1 + 0.5x^2 
s1 = Sinus 2 0.5 3 -- f(x) = 2 sin(0.5x + 3) 

-- ...............................................................

-- sample p1 from 0 to 5
m1 = doSampling p1  [0, 0.5 .. 5]
m2 = doSampling s1  [0, 0.5 .. 5]

-- ...............................................................
-- main
-- ...............................................................
main =  do
    putStrLn "Hello"
        print $ value p1 2
        print $ value s1 (pi/2)
        print $ pairs m1
        print $ pairs m2
Run Code Online (Sandbox Code Playgroud)

功能

module Function where    
-- ...............................................................
-- "class type"  : the types belonging to this family of types
--    must implement the following functions:
--          + value : takes a function and a real and returns a real
-- ...............................................................
class Function f where 
    value :: f -> Double -> Double
        -- f is a type variable, this is:
        -- f is a type of the Function "family" not an actual function
Run Code Online (Sandbox Code Playgroud)

样品

module Samples where

import Function

-- ...............................................................
-- Samples: new data type
-- This is the constructor and says it requieres
-- two list, one for the indexes (xs values) and another
-- for the values ( ys = f (xs) )
-- this constructor should not be used, instead use 
-- the "factory" function: new_Samples that performs some checks
-- ...............................................................
data Samples = Samples { indexes :: [Double] , values :: [Double] }
     deriving (Show)

-- ...............................................................
-- constructor: it checks lists are equal size, and indexes are sorted
new_Samples :: [Double] -> [Double] -> Samples
new_Samples ind val 
             | (length ind) /= (length val) = samplesVoid
             | not $ isSorted ind = samplesVoid
             | otherwise = Samples ind val

-- ...............................................................
-- sample a funcion
-- it takes a funcion f and a list of indexes and returns
-- a Samples calculating the values array as f(indexes)
doSampling :: (Function f) => f -> [Double] -> Samples
doSampling f ind = new_Samples ind vals
              where 
                    vals = [ value f x | x <- ind ]

-- ...............................................................
-- used as "error" in the construction
samplesVoid = Samples [] []

-- ...............................................................
size :: Samples -> Int
size samples = length (indexes samples)   
-- ...............................................................
-- utility function to get a pair (index,value) out of a Samples
pairs :: Samples -> [(Double, Double)]
pairs samples = pairs' (indexes samples) (values samples)

pairs' :: [Double] -> [Double] -> [(Double, Double)]
pairs' [] [] = []
pairs' [i] [v] = [(i,v)]
pairs' (i:is) (v:vs) = (i,v) : pairs' is vs


-- ...............................................................
-- to check whether a list is sorted (<)
isSorted :: (Ord t) => [t] -> Bool
isSorted [] = True
isSorted [e] = True
isSorted (e1:(e2:tail))
         | e1 < e2 = isSorted (e2:tail)
         | otherwise = False
Run Code Online (Sandbox Code Playgroud)

module Sinus where

-- ...............................................................
import Function

-- ...............................................................
-- Sinus: new data type
-- This is the constructor and says it requieres
-- a three reals
-- ...............................................................
data Sinus = Sinus { amplitude :: Double, frequency :: Double, phase :: Double }
     deriving (Show)

-- ...............................................................
-- we say that a Sinus is a Function (member of the class Function)
-- and then, how value is implemented
instance Function Sinus where
         value s x = (amplitude s) * sin ( (frequency s)*x + (phase s))
Run Code Online (Sandbox Code Playgroud)

多项式

module Polynomial where

-- ...............................................................
import Function

-- ...............................................................
-- Polynomial: new data type
-- This is the constructor and says it requieres
-- a list of coefficients
-- ...............................................................
data Polynomial = Polynomial { coeffs :: [Double] }
     deriving (Show)

-- ...............................................................
degree :: Polynomial -> Int
degree p = length (coeffs p)  - 1

-- ...............................................................
-- we say that a Polynomial is a Function (member of the class Function)
-- and then, how value is implemented
instance Function Polynomial where
         value p x = value' (coeffs p) x 1

--  list of coeffs -> x -> pw (power of x) -> Double
value' :: [Double] -> Double -> Double -> Double
value' (c:[]) _ pw =  c * pw
value' (c:cs) x pw =  (c * pw) + (value' cs x x*pw)
Run Code Online (Sandbox Code Playgroud)

Chr*_*lor 12

你当然不需要这Function门课.所有这些重量级的类,实例,成员变量fluff都是Haskell旨在避免的事情之一.纯函数可以更灵活.

这是一种做你想做的事情的简单方法.

type Sample = ([Double], [Double])

newSample xs vs
  | isSorted xs && length xs == length vs = (indices, values)
  | otherwise                             = ([], [])

pairs = uncurry zip

doSampling :: (Double -> Double) -> [Double] -> Sample
doSampling f xs = newSample xs (map f xs)

mkPolynomial :: [Double] -> (Double -> Double)
mkPolynomial coefs x = go coefs
  where
    go  []    = 0
    go (c:cs) = c + x * go cs

mkSinus :: Double -> Double -> Double -> (Double -> Double)
mkSinus amp freq phase x = amp * sin (freq * x + phase)

p1 = mkPolynomial [1, 0, 0.5] -- 1 + 0.5x^2
s1 = mkSinus 2 0.5 3          -- 2 sin(0.5x + 3)

m1 = doSampling p1 [0, 0.5 .. 5]
m2 = doSampling s1 [0, 0.5 .. 5]

main :: IO ()
main = do
  print $ p1 2
  print $ s1 (pi/2)
  print $ pairs m1
  print $ pairs m2
Run Code Online (Sandbox Code Playgroud)

  • 此解决方案向您展示如何在一般域上对一般函数进行采样.这些函数是*不透明的* - 你所能做的就是评估它们.如果您想对评估它们的功能做更多的工作,您确实需要单独携带这些信息.您可以通过将函数视为数据或引入类型类来实现.但是,我会从采样代码中保持正交,以便为您提供最大的灵活性. (2认同)