在Haskell中写这个Scala矩阵乘法
你能实现一个Matrix类和一个可以在两个矩阵上运行的*运算符吗?: scala> val x = Matrix(3,1,2,3,4,5,6) x: Matrix = [1.0,2.0,3.0] [4.0,5.0,6.0] scala> x*x.transpose res0: Matrix = [14.0,32.0] [32.0,77.0] 只是让人们不说这很难,这是Scala的实现(由Jonathan Merritt提供): class Matrix(els: List[List[Double]]) { /** elements of the matrix,stored as a list of its rows */ val elements: List[List[Double]] = els def nRows: Int = elements.length def nCols: Int = if (elements.isEmpty) 0 else elements.head.length /** all rows of the matrix must have the same number of columns */ require(elements.forall(_.length == nCols)) /* Add to each elem of matrix */ private def addRows(a: List[Double],b: List[Double]): List[Double] = List.map2(a,b)(_+_) private def subRows(a: List[Double],b: List[Double]):List[Double] = List.map2(a,b)(_-_) def +(other: Matrix): Matrix = { require((other.nRows == nRows) && (other.nCols == nCols)) new Matrix( List.map2(elements,other.elements) (addRows(_,_)) ) } def -(other: Matrix): Matrix = { require((other.nRows == nRows) && (other.nCols == nCols)) new Matrix( List.map2(elements,other.elements) (subRows(_,_)) ) } def transpose(): Matrix = new Matrix(List.transpose(elements)) private def dotVectors(a: List[Double],b: List[Double]): Double = { val multipliedElements = List.map2(a,b)(_*_) (0.0 /: multipliedElements)(_+_) } def *(other: Matrix): Matrix = { require(nCols == other.nRows) val t = other.transpose() new Matrix( for (row <- elements) yield { for (otherCol <- t.elements) yield dotVectors(row,otherCol) } ) override def toString(): String = { val rowStrings = for (row <- elements) yield row.mkString("[",","]") rowStrings.mkString("","n","n") } } /* Matrix constructor from a bunch of numbers */ object Matrix { def apply(nCols: Int,els: Double*):Matrix = { def splitRowsWorker( inList: List[Double],working: List[List[Double]]): List[List[Double]] = if (inList.isEmpty) working else { val (a,b) = inList.splitAt(nCols) splitRowsWorker(b,working + a) } def splitRows(inList: List[Double]) = splitRowsWorker(inList,List[List[Double]]()) val rows: List[List[Double]] = splitRows(els.toList) new Matrix(rows) } } 编辑我明白,严格来说答案是否定的:重载*不可能没有定义a和其他或特殊技巧的副作用. numeric-prelude package最佳描述:
解决方法
使用智能构造器和存储尺寸将非常安全.当然,对于操作signum和fromIntegral没有自然的实现(或者对角矩阵可能适用于后者).
module Matrix (Matrix(),matrix,matrixTranspose) where import Data.List (transpose) data Matrix a = Matrix {matrixN :: Int,matrixM :: Int,matrixElems :: [[a]]} deriving (Show,Eq) matrix :: Int -> Int -> [[a]] -> Matrix a matrix n m vals | length vals /= m = error "Wrong number of rows" | any (/=n) $map length vals = error "Column length mismatch" | otherwise = Matrix n m vals matrixTranspose (Matrix m n vals) = matrix n m (transpose vals) instance Num a => Num (Matrix a) where (+) (Matrix m n vals) (Matrix m' n' vals') | m/=m' = error "Row number mismatch" | n/=n' = error "Column number mismatch" | otherwise = Matrix m n (zipWith (zipWith (+)) vals vals') abs (Matrix m n vals) = Matrix m n (map (map abs) vals) negate (Matrix m n vals) = Matrix m n (map (map negate) vals) (*) (Matrix m n vals) (Matrix n' p vals') | n/=n' = error "Matrix dimension mismatch in multiplication" | otherwise = let tvals' = transpose vals' dot x y = sum $zipWith (*) x y result = map (col -> map (dot col) tvals') vals in Matrix m p result 在ghci中测试它: *Matrix> let a = matrix 3 2 [[1,2],[-1,1]] *Matrix> let b = matrix 2 3 [[3,1],[2,[1,0]] *Matrix> a*b Matrix {matrixN = 3,matrixM = 3,matrixElems = [[5,[4,2]]} 由于我的Num实例是通用的,它甚至适用于开箱即用的复杂矩阵: Prelude Data.Complex Matrix> let c = matrix 2 2 [[0:+1,1:+0],[5:+2,4:+3]] Prelude Data.Complex Matrix> let a = matrix 2 2 [[0:+1,4:+3]] Prelude Data.Complex Matrix> let b = matrix 2 3 [[3:+0,0]] Prelude Data.Complex Matrix> a Matrix {matrixN = 2,matrixM = 2,matrixElems = [[0.0 :+ 1.0,1.0 :+ 0.0],[5.0 :+ 2.0,4.0 :+ 3.0]]} Prelude Data.Complex Matrix> b Matrix {matrixN = 2,matrixElems = [[3.0 :+ 0.0,[2.0 :+ 0.0,[1.0 :+ 0.0,0.0 :+ 0.0]]} Prelude Data.Complex Matrix> a*b Matrix {matrixN = 2,matrixElems = [[2.0 :+ 3.0,1.0 :+ 1.0],[23.0 :+ 12.0,9.0 :+ 5.0]]} 编辑:新材料 哦,你想要在没有任何Num的情况下覆盖(*)函数.这可能是o但你必须记住Haskell标准库已保留(*)以便在Num类中使用. module Matrix where import qualified Prelude as P import Prelude hiding ((*)) import Data.List (transpose) class Multiply a where (*) :: a -> a -> a data Matrix a = Matrix {matrixN :: Int,Eq) matrix :: Int -> Int -> [[a]] -> Matrix a matrix n m vals | length vals /= m = error "Wrong number of rows" | any (/=n) $map length vals = error "Column length mismatch" | otherwise = Matrix n m vals matrixTranspose (Matrix m n vals) = matrix n m (transpose vals) instance P.Num a => Multiply (Matrix a) where (*) (Matrix m n vals) (Matrix n' p vals') | n/=n' = error "Matrix dimension mismatch in multiplication" | otherwise = let tvals' = transpose vals' dot x y = sum $zipWith (P.*) x y result = map (col -> map (dot col) tvals') vals in Matrix m p result a = matrix 3 2 [[1,3],6]] b = a * matrixTranspose 在ghci中测试: *Matrix> b Matrix {matrixN = 3,matrixElems = [[14,32],[32,77]]} 那里.现在,如果第三个模块想要同时使用Matrix版本的(*)和Prelude版本的(*),它当然必须导入一个或另一个合格的.但这只是照常营业. 我可以在没有Multiply类型类的情况下完成所有这些,但是这个实现使我们新的闪亮(*)在其他模块中打开以进行扩展. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |