Haskell:更好地理解代数数据类型
我试图构造一个表示多项式的代数数据类型。给定整型常数是多项式的定义,如果你加上两个多项式或乘上两个多项式,就会得到一个多项式 我很难理解代数数据类型通常是如何工作的,我甚至无法理解如何生成它。我现在有Haskell:更好地理解代数数据类型,haskell,algebraic-data-types,Haskell,Algebraic Data Types,我试图构造一个表示多项式的代数数据类型。给定整型常数是多项式的定义,如果你加上两个多项式或乘上两个多项式,就会得到一个多项式 我很难理解代数数据类型通常是如何工作的,我甚至无法理解如何生成它。我现在有 data Poly = Const Int | Add Poly Poly | Mult Poly Poly 然而,我甚至不知道这意味着什么,也不知道如何使用它,我只是从我见过的代数数据类型的例子中走出来 我见过这样的类型 data Tree
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>
*多项式>求值多边形1
:25:10:不在范围内:数据构造函数“Poly”
*多项式>
再次编辑:谢谢你所有的建议和帮助,它帮助我制作了一些符合我目的的东西 这是我发布的另一个解决方案的替代方案
你似乎想为多项式做一个ADT,我会用一个映射,但让我们用一个术语列表。首先是一些进口:
import Data.Function (on)
import Data.List (sortBy, groupBy, foldl1')
import qualified Data.Map as M
import Data.Function (on)
这样一来,多项式就是一个术语列表,首先按最高幂排序,一个术语是aX^n,由xan
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]
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
一旦定义了Num实例,我们就可以通过编写3*X^4
来创建x4
多项式的一个标准做法是用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
我希望能够映射系数,但我不想把它作为函子的一个例子,因为应用平方运算、三角函数或对数函数或逐项求平方根等运算是学生们的一个典型错误,而事实上,这些运算只有像标量乘法这样的一小部分,差异化和集成就是这样工作的。提供fmap会鼓励您执行类似于fmap(+1)
的操作,而不是(+(常量1))
我们需要对术语进行加法和乘法,并收集相似的术语。当我们收集相似项时,我们按幂的相反顺序排序,并忽略系数为零的项
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']
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']
你似乎想做一个多项式的ADT,但我更喜欢使用映射。首先是一些进口:
import Data.Function (on)
import Data.List (sortBy, groupBy, foldl1')
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 []
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]
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的一个特定值来计算它
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
此处的折叠采用部分计算的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
如果我们想使用映射函数,我们可以将它从mapna
提升到polyna
我希望能够映射系数,但我不想把它作为函子的一个例子,因为应用平方运算、三角函数或对数函数或逐项求平方根等运算是学生们的一个典型错误,而事实上,这些运算只有像标量乘法这样的一小部分,差异化和集成就是这样工作的。提供fmap会鼓励您执行类似于
fmap(+1)
的操作,而不是(+(常量1))
贴图已自动收集相似项,但我们希望忽略系数为零的项:
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 []) = 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']
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']
你会问自己,你想对多项式执行什么样的操作,然后尝试实现它们。@augustss我有一些我以后需要实现的操作,但现在我想重点了解它的定义以及如何使用定义。你把类型(
Poly
)和它的构造函数搞混了(Const
,Add
,Int
)Const 1
构造一个Poly
;Poly 1
不能t@bheklilr啊,谢谢,这很有意义。我想为了这个小程序的目的,我不需要让多边形发挥多边形的力量,只需要显示多边形。谢谢你的帮助!FWIW,你可能会更好地表示多项式(在单个变量中)作为元组列表:x^2+2x^7=[(1,2)、(2,7)]:[(系数,指数)]
,比如说。