繁体   English   中英

Haskell:更好地理解代数数据类型

[英]Haskell: Understanding algebraic data types better

我正在尝试构造一个代表多项式的代数数据类型。 给定一个整数常量是多项式的定义,并且如果添加两个多项式或乘以两个多项式,则会得到多项式。

我很难理解代数数据类型的工作原理以及我如何制作这个数据类型。 我现在有

data Poly = Const Int | 
            Add Poly Poly | 
            Mult Poly Poly

但是我不知道这甚至意味着什么或者如何使用它,我只是简单地讨论了代数数据类型的例子。

我见过类似的类型

data Tree = NullT |
            Node Int Tree Tree

这对我来说更有意义,以及如何使用它。 多项式的例子看起来很抽象,我不知道从哪里开始。

编辑:当我尝试实现简单的测试功能,如:

evalPoly :: Poly -> Int
evalPoly (Const n) = n

我遇到了错误

*Polynomial> evalPoly Poly 1

<interactive>:25:10: Not in scope: data constructor ‘Poly’
*Polynomial> 

再次编辑:感谢您的所有建议和帮助,这有助于我制作出符合我目的的东西!

这是我发布的另一个解决方案。

你似乎想为多项式做一个ADT,在那里我使用Map,但让我们看一下术语列表。 首先进口一些:

import Data.Function (on)
import Data.List (sortBy, groupBy, foldl1')

这样,多项式是项列表,首先以最高功率排序,并且项是aX ^ n,由X an

newtype Poly a n = Poly {terms :: [Term a n]} deriving (Show)
data Term a n = X {coeff :: a, power :: n}  deriving (Eq,Show)

让我们做一些简单的多项式:

zero = Poly []
x = Poly [X 1 1]

constant :: (Num a,Eq a,Num n) => a -> Poly a n
constant 0 = zero
constant a = Poly [X a 0]

一旦我们定义了Num实例,我们就可以通过编写3*x^4来生成X 3 4 3*x^4

与多项式有关的标准事项是使用x的特定值来评估它。

subst :: (Num a, Integral n) => a -> Term a n -> a
subst x (X a n) = a * x ^ n

evalAt :: (Num a, Integral n) => a -> Poly a n -> a
evalAt x = sum . map (subst x) . terms

我希望能够映射系数,但我不想让它成为Functor的一个实例,因为应用平方,应用三角函数或对数函数或按术语取平方根等操作是一个经典的学生错误实际上,只有像标量乘法,微分和积分这样的少数事情就像这样。 提供fmap会鼓励你做一些像fmap (+1)而不是(+ (constant 1))

mapCoeffs :: (a -> b) -> Poly a n -> Poly b n
mapCoeffs f = Poly . map f' . terms
  where f' (X a n) = X (f a) n

我们需要添加和倍增术语,并收集类似的术语。 当我们收集类似的术语时,我们按功率的相反顺序排序并省略具有零系数的术语。

addTerm (X a n) (X b m) | n == m = X (a+b) n
                        | otherwise = error "addTerm: mismatched powers" 
multTerm (X a n) (X b m) = X (a*b) (n+m)

collectLikeTerms :: (Num a, Ord n, Eq a) => Poly a n -> Poly a n
collectLikeTerms =  Poly . filter ((/= 0).coeff)            -- no zero coeffs
                         . map (foldl1' addTerm)            -- add the like powers
                         . groupBy ((==) `on` power)        -- group the like powers
                         . sortBy (flip compare `on` power) -- sort in reverse powers
                         . terms                            

现在我们可以创建实例:

instance (Eq a,Num a,Ord n,Num n) => Eq (Poly a n) where
   f == g = f - g == zero

instance (Eq a,Num a,Num n,Ord n) => Num (Poly a n) where
   fromInteger = constant . fromInteger
   signum (Poly []) = zero
   signum (Poly (t:_)) = constant . signum . coeff $ t
   abs =  mapCoeffs abs
   negate = mapCoeffs negate
   (+) = (collectLikeTerms.) . (Poly.) . ((++) `on` terms)
   (Poly ts) * (Poly ts') = collectLikeTerms $ Poly [multTerm t t' | t<-ts, t'<-ts']

在行动:

ghci> 5*x^2 + 6*x^7 + 2
Poly {terms = [X {coeff = 6, power = 7},X {coeff = 5, power = 2},X {coeff = 2, power = 0}]}

你似乎想为多项式做一个ADT,但我更喜欢使用Map。 首先进口一些:

import qualified Data.Map as M
import Data.Function (on)

多项式是从x的幂到系数的映射。

newtype Poly a n = Poly {coeffMap :: M.Map n a} deriving (Show)
lift f = Poly . f . coeffMap

让我们做一些简单的多项式:

zero = Poly M.empty                  -- none of the powers have non-zero coefficients
x = Poly $ M.singleton 1 1           -- x^1 has coefficient 1

constant 0 = zero
constant a = Poly $ M.singleton 0 a  -- x^0 has coefficient a

与多项式有关的标准事项是使用x的特定值来评估它。

这里的折叠采用部分计算的b并添加新项, a*x^n

evalAt :: (Num a, Integral n) => a -> Poly a n -> a
evalAt x = M.foldrWithKey (\n a b -> b + a*x^n) 0 . coeffMap

如果我们想使用Map函数,我们可以将它从Map na提升到Poly na
我希望能够映射系数,但我不想让它成为Functor的一个实例,因为应用平方,应用三角函数或对数函数或按术语取平方根等操作是一个经典的学生错误实际上,只有像标量乘法,微分和积分这样的少数事情就像这样。 提供fmap会鼓励你做一些像fmap (+1)而不是(+ (constant 1))

mapCoeffs :: (a -> b) -> Poly a n -> Poly b n
mapCoeffs f = lift (fmap f)

地图已经自动收集了相似的字词,但我们要省略零系数的字词:

strikeZeros :: (Num a,Eq a) => Poly a n -> Poly a n
strikeZeros =  lift $  M.filter (/= 0)                      

现在我们可以创建实例:

instance (Eq a,Num a,Ord n,Num n) => Eq (Poly a n) where
   f == g = f - g == zero

instance (Eq a,Num a,Num n,Ord n) => Num (Poly a n) where
   fromInteger = constant . fromInteger
   signum (Poly m) | M.null m = zero
                   | otherwise = let (n,a) = M.findMax m in 
                        Poly $ M.singleton n (signum a)
   abs =  mapCoeffs abs
   negate = mapCoeffs negate
   (+) = (strikeZeros.) . (Poly.) . ((M.unionWith (+)) `on` coeffMap)
   (Poly m) * (Poly m') = Poly $ 
          M.fromListWith (+) [(n+n',a*a') | (n,a)<-M.assocs m, (n',a')<-M.assocs m']

在行动:

ghci> 3*x^4 + 6 + 2*x^7
Poly {coeffMap = fromList [(0,6),(4,3),(7,2)]}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM