在Haskell中表示可交换半群多项式

时间:2017-09-21 10:51:52

标签: haskell symbolic-math algebra

我正在尝试代表以下字词:

a0 <> a1 <> ... <> an-1

其中ai必须是可交换半群的元素。为此,可以选择如下的数据表示:

newtype SemigroupPolynomial a = SP Map a Integer

其中地图包含多项式的不同项及其计数。

通过这种方式,我们可以代表总和

3 + 3 + 6

as(假设OverloadedLists):

SP [(3, 2), (6, 1)]

但我们也可以代表以下术语:

3 * 3 * 6

SemigroupPolynomial可以是Semigroup的实例:

instance ??? a => Semigroup (SemigroupPolynomial a) where
    (MP p0) <> (MP p1) = 
        MP $ Map.filter (0/=) $ Map.unionWith (+) p0 p1

问题不在于我必须在???中加入哪些限制因素:

  1. <>操作是可交换的和关联的。
  2. 它可用于表示总和和产品,如上所示。
  3. 关于如何表示可交换的Monoids was already asked here的类似问题。但是,似乎约束(Abelian m, Monoidal m)可能过于强大(我不需要零元素),它会阻止我使用它来表示产品。

2 个答案:

答案 0 :(得分:1)

正如@leftroundabout所评论的那样,你在这里 需要约束。不要被#34;约束&#34;这个词所迷惑。在Haskell中,约束的主要目的不是以某种方式约束特定类型或操作的行为。相反,它将函数将接受的类型集约束到支持一组操作的那些类型。

当我写:

f

我并没有真正限制类型fmapTwice充当仿函数并遵守仿函数所需的规则。相反,我限制f函数仅适用于支持fmap操作的类型data Foo a = Foo a | NoFoo deriving (Show) instance Functor Foo where fmap _ _ = NoFoo -- invalid functor violates: fmap id = id

没有什么可以阻止一些人写作:

> fmapTwice (*2) (Foo 10)
NoFoo
>

并将我的函数应用于此无效仿函数:

Functor

Haskell依赖程序员规则来确保声明具有import Data.Semigroup import qualified Data.Map as Map import Data.Map.Strict (Map) data SemigroupPolynomial a = SP (Map a Integer) deriving (Show) instance (Ord a) => Semigroup (SemigroupPolynomial a) where (SP p0) <> (SP p1) = SP $ Map.filter (0/=) $ Map.unionWith (+) p0 p1 实例的东西是一个表现良好的函子。

在您的示例中,实例:

Ord a

不需要a以外的任何约束,以确保Map可以用作SemigroupPolynomial密钥。

现在,您可以确保只使用foldSP :: (a -> a -> a) -> SemigroupPolynomial a -> a foldSP f (SP m) = foldr1 f $ concatMap (\(a, n) -> replicate (fromIntegral n) a) (Map.assocs m) main = do let sp = singleton 3 <> singleton 3 <> singleton 6 print sp print $ foldSP (*) sp print $ foldSP (+) sp print $ foldSP (-) sp -- wrong, but it's your own damn fault 代表交换操作:

data CommutativeOp a = CO (a -> a -> a)
foldSP :: CommutativeOp a -> SemigroupPolynomial a -> a
foldSP (CO f) (SP m) = <same as above>

如果你想以某种方式在你的数据类型中引入交换性的要求,那么这样做的一种方式(它不涉及Haskell&#34;约束&#34;根本就是)写出如下内容: / p>

plusOp = CO (+)
timesOp = CO (*)

现在,只要你在写作时意识到:

(+)

您正在声明(*)foldSP是可转换操作,这将确保main = do let sp = singleton 3 <> singleton 3 <> singleton 6 print $ foldSP plusOp sp print $ foldSP timesOp sp 仅适用于此类操作:

a

如果您想以某种方式在类型SemigroupPolynomial a上引入交换约束以确保a是有效的表示,那么您不能为Int执行此操作等于Int -> Int -> Int,显然,因为它取决于使用哪个二进制操作newtype进行折叠。

相反,您需要将操作嵌入到类型中,可能使用代表操作的Sum,例如Product中的Data.Semigroupclass Commutative a instance Commutative (Sum a) instance Commutative (Product a) instance (Ord a, Commutative b) => SemigroupPolynomial b where ...definition on (<>) as above... 。然后,您可以引入一个类型类(没有操作)来表示交换约束:

foldSP' :: (Monoid a) => SemigroupPolynomial a -> a
foldSP' (SP m) = mconcat $ concatMap (\(a, n) -> replicate (fromIntegral n) a)
                                     (Map.assocs m)

现在fold操作将使用newtype中隐含的操作(这里,只使用monoid实例):

import Data.Semigroup
import qualified Data.Map as Map
import Data.Map.Strict (Map)

newtype SemigroupPolynomial a = SP (Map a Integer) deriving (Show)

class Commutative a
instance Commutative (Sum a)
instance Commutative (Product a)
instance (Ord a, Commutative a) => Semigroup (SemigroupPolynomial a) where
    (SP p0) <> (SP p1) = 
        SP $ Map.filter (0/=) $ Map.unionWith (+) p0 p1

singleton :: a -> SemigroupPolynomial a
singleton x = SP $ Map.singleton x 1

foldSP' :: (Monoid a) => SemigroupPolynomial a -> a
foldSP' (SP m) = mconcat $ concatMap (\(a, n) -> replicate (fromIntegral n) a)
                                     (Map.assocs m)

main = do let sp1 = singleton (Sum 3) <> singleton (Sum 3) <> singleton (Sum 6)
          print sp1
          print (foldSP' sp1)
          let sp2 = singleton (Product 3) <> singleton (Product 3) 
                          <> singleton (Product 6)
          print sp2
          print (foldSP' sp2)

也许这就是你想要的。如果是这样,完整的示例如下所示:

phpMyAdmin

答案 1 :(得分:1)

你所构建的是一个免费的幺半群,而这只是一个列表。您可以使用[(a, Integer)]而不是地图,这对某些用例来说没问题。由于Haskell没有依赖类型,我怀疑在类型方面会有更好的东西。

  

问题不在于我必须加入哪些限制?这样:

     

&lt;&gt;操作是可交换的和联想的。

     

它可用于表示总和和产品,如上所示。

您应该创建Ring类型类,或使用numeric-prelude的类型类。这将让用户知道您的数据类型是关联的,并且具有加法和乘法。根据我的知识,没有任何伟大的类型级方式来表明这些。您可以使用属性测试来确保一切都通过hspec或类似的库工作。

  

然而似乎约束(Abelian m,Monoidal m)可能太强(我不需要零元素),它会阻止我用它来代表产品。

没有与多项式“超过”半群相关联的代数结构,因此您可能必须自己实现。由于您说<>是可交换的,因此您的元素满足Abelian m。这只是添加单位的问题。