Haskell 中的矩阵构造函数和方法

发布于 2024-12-07 20:33:56 字数 948 浏览 0 评论 0原文

所以这里是一个嵌套列表 [[1, 2], [3, 4]]

类 Eq、Num 和 Show 的实例

我想将它包装在一个名为 Matrix 的类型中,并使其成为我已经创建的 (添加、sub、mul)嵌套列表(矩阵)的运算。如何重载 (+ - *) 运算符,以便 + 映射到 add,- 映射到 sub,* 映射到 mul?所以我可以做到这一点

> ma = Matrix [[1, 2], [3, 4]]
> mb = Matrix [[5, 6], [7, 8]]
> ma + mb
> ma - mb
> ma * mb

谢谢

编辑

这是我迄今为止的尝试

> add = zipWith (zipWith (+))
> sub = zipWith (zipWith (-))
> data Matrix a = Matrix [[a]] deriving (Eq, Show)
> instance Num (Matrix a)
>   where
>   (+) x y = Matrix $ add x y
>   (-) x y = Matrix $ sub x y

得到的

 Couldn't match expected type `[[c0]]' with actual type `Matrix a'
    In the first argument of `sub', namely `x'
    In the second argument of `($)', namely `sub x y'
    In the expression: Matrix $ sub x y

这是我从 ghci编辑#2 我现在需要弄清楚的最后一件事是

如何打印

1 2
3 4

而不是 Matrix [[1,2],[3,4]]

So here is a nested list [[1, 2], [3, 4]]

I want to wrap it in a type called Matrix, and make it an instance of the classes Eq, Num, and Show

I have already created (add, sub, mul) operations for nested lists (matrices). How do I overload (+ - *) operators so that + maps to add, - maps to sub, and * maps to mul? So I can do this

> ma = Matrix [[1, 2], [3, 4]]
> mb = Matrix [[5, 6], [7, 8]]
> ma + mb
> ma - mb
> ma * mb

Thanks

EDIT

this is my attempt so far

> add = zipWith (zipWith (+))
> sub = zipWith (zipWith (-))
> data Matrix a = Matrix [[a]] deriving (Eq, Show)
> instance Num (Matrix a)
>   where
>   (+) x y = Matrix $ add x y
>   (-) x y = Matrix $ sub x y

This is what I get from ghci

 Couldn't match expected type `[[c0]]' with actual type `Matrix a'
    In the first argument of `sub', namely `x'
    In the second argument of `($)', namely `sub x y'
    In the expression: Matrix $ sub x y

EDIT #2
The last thing I need to figure out right now is

how to I print

1 2
3 4

Instead of Matrix [[1,2],[3,4]]

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

熟人话多 2024-12-14 20:33:56

您在为您的类型定义 Num 实例时遇到问题吗?尝试以下代码:

data Matrix a = Matrix [[a]]
              deriving (Eq)

plus_mat :: Num a => [[a]] -> [[a]] -> [[a]]
plus_mat = zipWith (zipWith (+))

instance Num a => Num (Matrix a)
  where
    (Matrix a) + (Matrix b) = Matrix $ plus_mat a b
    (-)                     = undefined
    (*)                     = undefined
    negate                  = undefined
    abs                     = undefined
    signum                  = undefined
    fromInteger             = undefined

测试:

*Main> Matrix [[1,2],[3,4]] + Matrix [[5,6],[7,8]]
Matrix [[6,8],[10,12]]

其余类方法的定义留作练习。

这是 MatrixShow 实例:

import Data.List

instance Show a => Show (Matrix a)
  where
    show (Matrix a) = intercalate "\n" $ map (intercalate " " . map show) a

测试:

*Main Data.List> Matrix [[1,2,3], [4,5,6]]
1 2 3
4 5 6

Are you having a problem with defining a Num instance for your type? Try this code:

data Matrix a = Matrix [[a]]
              deriving (Eq)

plus_mat :: Num a => [[a]] -> [[a]] -> [[a]]
plus_mat = zipWith (zipWith (+))

instance Num a => Num (Matrix a)
  where
    (Matrix a) + (Matrix b) = Matrix $ plus_mat a b
    (-)                     = undefined
    (*)                     = undefined
    negate                  = undefined
    abs                     = undefined
    signum                  = undefined
    fromInteger             = undefined

Testing:

*Main> Matrix [[1,2],[3,4]] + Matrix [[5,6],[7,8]]
Matrix [[6,8],[10,12]]

Definitions of the remaining class methods are left as exercise.

And here's a Show instance for Matrix:

import Data.List

instance Show a => Show (Matrix a)
  where
    show (Matrix a) = intercalate "\n" $ map (intercalate " " . map show) a

Testing:

*Main Data.List> Matrix [[1,2,3], [4,5,6]]
1 2 3
4 5 6
忆沫 2024-12-14 20:33:56

如果您检查 addsub 的类型,您将看到该问题。

ghci> :t add
add :: Num a => [[a]] -> [[a]] -> [[a]]
ghci> :t sub
sub :: Num a => [[a]] -> [[a]] -> [[a]]

Mikhail 的建议是从本质上解开 2D 列表并在 Num 实例方法中重新包装它。另一种方法是修改 addsub 方法来处理矩阵。在这里,我使用“提升”方法,编写组合器将函数从一种类型“提升”到另一种类型。

-- unwraps the 2d list from a matrix
unMatrix :: Matrix a -> [[a]]
unMatrix (Matrix m) = m

-- lifts a 2d list operation to be a Matrix operation
liftMatrixOp :: ([[a]] -> [[a]] -> [[a]]) -> Matrix a -> Matrix a -> Matrix a
liftMatrixOp f x y = Matrix $ f (unMatrix x) (unMatrix y)

-- lifts a regular operation to be a 2d list operation
lift2dOp :: (a -> a -> a) -> [[a]] -> [[a]] -> [[a]]
lift2dOp f = zipWith (zipWith f)

使用这些组合器,定义 addsub 只需适当提升即可。

add, sub :: Num a => Matrix a -> Matrix a -> Matrix a
add = liftMatrixOp add2D
sub = liftMatrixOp sub2D

add2D, sub2D :: Num a => [[a]] -> [[a]] -> [[a]]
add2D = lift2dOp (+)
sub2D = lift2dOp (-)

现在我们有了适用于矩阵的函数,Num 实例很简单

instance (Num a) => Num (Matrix a) where
  (+) = add
  (-) = sub
  ..etc..

当然我们可以将 lift2dOp 和 liftMatrixOp 组合成一个方便的函数:

-- lifts a regular operation to be a Matrix operation
liftMatrixOp' :: (a -> a -> a) -> Matrix a -> Matrix a -> Matrix a
liftMatrixOp' = liftMatrixOp . lift2dOp

instance (Num a) => Num (Matrix a) where
  (+) = liftMatrixOp' (+)
  (-) = liftMatrixOp' (-)
  (*) = liftMatrixOp' (*)
  ..etc..

现在您尝试:定义 liftMatrix :: (a -> a) ->;矩阵a->矩阵 a,一元函数的提升函数。现在用它来定义 negateabssignum。文档建议 abs x * signum x 应始终等于 x。看看我们的实现是否如此。

ghci> quickCheck (\xs -> let m = Matrix xs in abs m * signum m == m)
+++ OK, passed 100 tests.

事实上,如果您使用更宽松的类型签名编写 liftMatrix,它可以用于为矩阵定义一个 Functor 实例。

liftMatrix :: (a -> b) -> Matrix a -> Matrix b

instance Functor (Matrix a) where
  fmap = liftMatrix

现在考虑一下如何实现 fromInteger。实现这个可以让你在 ghci 中做这样的事情:

ghci> Matrix [[1,2],[3,4]] + 1
Matrix [[2,3],[4,5]]

无论如何,这就是我实现它的方式。请记住,Haskell 代码中的任何数字文字 n 实际上都会转换为 fromInteger n,这就是它起作用的原因。

我认为现在这已经足够有趣了,但是如果您需要更多练习,请尝试熟悉这个矩阵的 Arbitrary 实例:

instance Arbitrary a => Arbitrary (Matrix a) where
  arbitrary = liftM Matrix arbitrary

If you inspect the type of your add and sub, you will see the issue.

ghci> :t add
add :: Num a => [[a]] -> [[a]] -> [[a]]
ghci> :t sub
sub :: Num a => [[a]] -> [[a]] -> [[a]]

Mikhail's suggestion was to essentially unwrap the 2D list and rewrap it in the Num instance methods. Another way to do this is to modify your add and sub methods to work on Matrices instead. Here I use a "lifting" approach, where I write combinators to "lift" a function from one type to another.

-- unwraps the 2d list from a matrix
unMatrix :: Matrix a -> [[a]]
unMatrix (Matrix m) = m

-- lifts a 2d list operation to be a Matrix operation
liftMatrixOp :: ([[a]] -> [[a]] -> [[a]]) -> Matrix a -> Matrix a -> Matrix a
liftMatrixOp f x y = Matrix $ f (unMatrix x) (unMatrix y)

-- lifts a regular operation to be a 2d list operation
lift2dOp :: (a -> a -> a) -> [[a]] -> [[a]] -> [[a]]
lift2dOp f = zipWith (zipWith f)

With these combinators, defining add and sub is simply a matter of lifting appropriately.

add, sub :: Num a => Matrix a -> Matrix a -> Matrix a
add = liftMatrixOp add2D
sub = liftMatrixOp sub2D

add2D, sub2D :: Num a => [[a]] -> [[a]] -> [[a]]
add2D = lift2dOp (+)
sub2D = lift2dOp (-)

Now that we have functions that work on Matrices, the Num instance is simple

instance (Num a) => Num (Matrix a) where
  (+) = add
  (-) = sub
  ..etc..

Of course we could have combined lift2dOp and liftMatrixOp into one convenience function:

-- lifts a regular operation to be a Matrix operation
liftMatrixOp' :: (a -> a -> a) -> Matrix a -> Matrix a -> Matrix a
liftMatrixOp' = liftMatrixOp . lift2dOp

instance (Num a) => Num (Matrix a) where
  (+) = liftMatrixOp' (+)
  (-) = liftMatrixOp' (-)
  (*) = liftMatrixOp' (*)
  ..etc..

Now you try: define liftMatrix :: (a -> a) -> Matrix a -> Matrix a, a lifting function for unary functions. Now use that to define negate, abs, and signum. The docs suggest that abs x * signum x should always be equivalent to x. See if this is true for our implementation.

ghci> quickCheck (\xs -> let m = Matrix xs in abs m * signum m == m)
+++ OK, passed 100 tests.

In fact, if you write liftMatrix with the more lenient type signature, it can be used to define a Functor instance for Matrices.

liftMatrix :: (a -> b) -> Matrix a -> Matrix b

instance Functor (Matrix a) where
  fmap = liftMatrix

Now think about how you could implement fromInteger. Implementing this allows you to do stuff like this in ghci:

ghci> Matrix [[1,2],[3,4]] + 1
Matrix [[2,3],[4,5]]

That's how it works the way I implemented it, anyways. Remember that any numeric literal n in Haskell code is actually transformed into fromInteger n, which is why this works.

I think that's enough fun for now, but if you need more exercises, try getting comfortable with this Arbitrary instance of Matrices:

instance Arbitrary a => Arbitrary (Matrix a) where
  arbitrary = liftM Matrix arbitrary
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文