Матричный конструктор и метод в Haskell - PullRequest
4 голосов
/ 05 октября 2011

Итак, вот вложенный список [[1, 2], [3, 4]]

Я хочу обернуть его в тип с именем Matrix и сделать его экземпляром классов Eq, Num и Show

Я уже создал (add, sub, mul) операции для вложенных списков (матриц). Как перегрузить операторы (+ - *), чтобы добавить + maps для добавления, - сопоставить с sub и * сопоставить с mul? Так что я могу сделать это

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

Спасибо

EDIT

пока это моя попытка

> 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

Это то, что я получаю от 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

РЕДАКТИРОВАТЬ # 2 Последнее, что мне нужно выяснить прямо сейчас, это

как печатать

1 2
3 4

Вместо матрицы [[1,2], [3,4]]

Ответы [ 2 ]

7 голосов
/ 05 октября 2011

Если вы проверите тип ваших add и sub, вы увидите проблему.

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

Михаилу было предложено по существу развернуть 2D-список и перенести его в методы экземпляра Num.,Еще один способ сделать это - изменить ваши методы add и sub для работы с матрицами.Здесь я использую подход «подъема», когда я пишу комбинаторы, чтобы «поднять» функцию из одного типа в другой.

-- 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)

С этими комбинаторами определение add и sub просто вопросподъема соответственно.

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) -> Matrix a -> Matrix a, функцию подъема для унарных функций.Теперь используйте это для определения negate, abs и signum.Документы предполагают, что abs x * signum x всегда должно быть эквивалентно x.Посмотрите, верно ли это для нашей реализации.

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

На самом деле, если вы напишите liftMatrix с более мягкой подписью типа, это можно использовать для определения экземпляра Functor для Matrices.

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]]

Вот так все работает так, как я это реализовал, в любом случае.Помните, что любой числовой литерал n в коде на Haskell фактически преобразуется в fromInteger n, поэтому это работает.

Я думаю, на данный момент это достаточно весело, но если вам нужно больше упражнений, попробуйте освоитьсяэто Arbitrary экземпляр Matrices:

instance Arbitrary a => Arbitrary (Matrix a) where
  arbitrary = liftM Matrix arbitrary
6 голосов
/ 05 октября 2011

У вас проблемы с определением экземпляра 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]]

Определения оставшихся методов класса оставлены в качестве упражнения.

А вот Show экземпляр для Matrix:

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
...