我用foldl1 (+)
对矩阵列表求和,因为我注意到sum
实际上将左上角元素的总和作为1x1矩阵返回。
$ stack exec --resolver lts-12.5 --package matrix -- ghci
GHCi, version 8.4.3: http://www.haskell.org/ghc/ :? for help
Prelude> import Data.Matrix
Prelude Data.Matrix> t = [identity 2, identity 2] -- two 2x2 identity matrices
Prelude Data.Matrix> foldl1 (+) t
┌ ┐
│ 2 0 │
│ 0 2 │
└ ┘
Prelude Data.Matrix> sum t
┌ ┐
│ 2 │
└ ┘
这对我来说是出乎意料的,LYAH建议sum = foldl1 (+)
¹,甚至hlint建议我使用sum
而不是foldl1 (+)
,因为这是“消除[]
上的错误”(附带问题:如何优雅地和[]
安全地汇总矩阵?)
为什么sum /= foldl1 (+)
用于矩阵,为什么通常不总是sum == foldl1 (+)
?
或者,排除空列表的情况:
为什么不是sum == foldl neutralElement (+)
?还是sum == foldl (+) (zero 2 2)
上的[identity 2, identity 2]
?
自己的作品:
Prelude Data.Matrix> :t sum
sum :: (Foldable t, Num a) => t a -> a
Prelude Data.Matrix> :info sum
class Foldable (t :: * -> *) where
...
sum :: Num a => t a -> a
...
-- Defined in ‘Data.Foldable’
sum
的来源是:
sum :: Num a => t a -> a
sum = getSum #. foldMap Sum
Matrix
的实例是:
instance Foldable Matrix where
foldMap f = foldMap f . mvect
instance Num a => Num (Matrix a) where
fromInteger = M 1 1 . V.singleton . fromInteger
negate = fmap negate
abs = fmap abs
signum = fmap signum
-- Addition of matrices.
{-# SPECIALIZE (+) :: Matrix Double -> Matrix Double -> Matrix Double #-}
{-# SPECIALIZE (+) :: Matrix Int -> Matrix Int -> Matrix Int #-}
(M n m v) + (M n' m' v')
-- Checking that sizes match...
| n /= n' || m /= m' = error $ "Addition of " ++ sizeStr n m ++ " and "
++ sizeStr n' m' ++ " matrices."
-- Otherwise, trivial zip.
| otherwise = M n m $ V.zipWith (+) v v'
-- Substraction of matrices.
...
-- Multiplication of matrices.
...
¹在加整数的情况下
答案 0 :(得分:9)
因为Data.Matrix
的{{1}}实例通过返回1x1矩阵来实现Num
,并且通过添加fromInteger
来实现+
,该方法将正确的矩阵截断为大小左边的一个(如果左边大于右边则出错)。
elementwise
是sum
,它以foldl (+) 0
中的1x1矩阵开始,并将列表中的所有矩阵都截断为该大小。 0
不必由整数组成矩阵,因此结果就是列表中最小矩阵的大小。
此外,foldl1 (+)
是默认的sum = getSum #. foldMap Sum
实现,但列表实例将其覆盖为Foldable
。
直到sum = foldl (+) 0
的{{1}}实现(如上所示),直到版本0.3.1.1,如果操作数的形状不同,都会产生错误,但是在版本0.3.2.0中,它假定它们是相同的。相同的形状,无需检查,在版本0.3.3.0中再次更改(根据评论):
Data.Matrix
实现似乎与当前版本0.3.6.1相同
答案 1 :(得分:2)
我认为@pat已很好地回答了这个问题,但我会尝试回答附带的问题:
我如何优雅地[]安全地汇总矩阵?
您不能安全地汇总一个矩阵列表,因为您不确定列表中每个矩阵的大小是多少,除非您在类型级别拥有该大小信息。假设类型为data Matrix (m :: Nat) (n :: Nat) e = ...
,那么可以保证列表中的每个矩阵都具有完全相同的维。我不确定通用数组库在该领域是否有任何工作,但是我已经在OpenCV绑定和mapalgebra中看到了这种方法,并简要考虑过在{{3}中使用它}。这种方法有一些缺点,但这对这个问题并不重要。
另一种方法是不使用矩阵列表,而是使用3D数组,该数组本质上是大小相同的2D矩阵的页面序列。显然,matrix
软件包不支持更大的尺寸,因此您无法使用该库来实现。我将展示如何使用massiv来完成。考虑我们有这个3D阵列a
:
λ> a = makeArrayR D Par (Sz3 3 4 5) $ \(i :> j :. k) -> i + j * k
λ> a
Array D Par (Sz (3 :> 4 :. 5))
[ [ [ 0, 0, 0, 0, 0 ]
, [ 0, 1, 2, 3, 4 ]
, [ 0, 2, 4, 6, 8 ]
, [ 0, 3, 6, 9, 12 ]
]
, [ [ 1, 1, 1, 1, 1 ]
, [ 1, 2, 3, 4, 5 ]
, [ 1, 3, 5, 7, 9 ]
, [ 1, 4, 7, 10, 13 ]
]
, [ [ 2, 2, 2, 2, 2 ]
, [ 2, 3, 4, 5, 6 ]
, [ 2, 4, 6, 8, 10 ]
, [ 2, 5, 8, 11, 14 ]
]
]
然后我们可以使用foldlWithin
来折叠数组的任何维度,同时降低其维度:
λ> foldlWithin Dim3 (+) 0 a
Array D Par (Sz (4 :. 5))
[ [ 3, 3, 3, 3, 3 ]
, [ 3, 6, 9, 12, 15 ]
, [ 3, 9, 15, 21, 27 ]
, [ 3, 12, 21, 30, 39 ]
]
λ> foldlWithin Dim1 (+) 0 a
Array D Par (Sz (3 :. 4))
[ [ 0, 10, 20, 30 ]
, [ 5, 15, 25, 35 ]
, [ 10, 20, 30, 40 ]
]
请注意,在这里的任何时候都不会引发异常或其他不可预测的行为(当然,不放置异步异常)。另外,也可以截取数组的切片,然后单独添加,但是如果我们想避免出现异常,则该方法会涉及更多点:
λ> a !?> 0
Array D Par (Sz (4 :. 5))
[ [ 0, 0, 0, 0, 0 ]
, [ 0, 1, 2, 3, 4 ]
, [ 0, 2, 4, 6, 8 ]
, [ 0, 3, 6, 9, 12 ]
]
λ> import Data.Maybe
λ> fromMaybe empty $ a !?> 0 >>= \acc0 -> foldlM (\acc pageIx -> (acc +) <$> (a !?> pageIx)) acc0 (1 ... 2)
Array D Par (Sz (4 :. 5))
[ [ 3, 3, 3, 3, 3 ]
, [ 3, 6, 9, 12, 15 ]
, [ 3, 9, 15, 21, 27 ]
, [ 3, 12, 21, 30, 39 ]
]
如果Nothing
没有索引为a
到0
的页面,则将产生2
。另一方面,如果您可以接受运行时异常,那么这会更清楚一些,但不太安全:
λ> foldlS (\acc pageIx -> acc + (a !> pageIx)) (a !> 0) (1 ... 2)
Array D Par (Sz (4 :. 5))
[ [ 3, 3, 3, 3, 3 ]
, [ 3, 6, 9, 12, 15 ]
, [ 3, 9, 15, 21, 27 ]
, [ 3, 12, 21, 30, 39 ]
]
或使用列表,如实际问题中所述:
λ> Prelude.foldl1 (+) $ Prelude.fmap (a !>) [0 .. 2]
Array D Par (Sz (4 :. 5))
[ [ 3, 3, 3, 3, 3 ]
, [ 3, 6, 9, 12, 15 ]
, [ 3, 9, 15, 21, 27 ]
, [ 3, 12, 21, 30, 39 ]
]
旁注。我注意到SECIALIZE
用法的用法,这使我相信您实际上在乎性能。一个小而重要的事实是:matrix
软件包在Matrix
数据类型的下面使用了装箱的向量,这将始终为您提供出色的性能,而杂用程序无法帮助您。
答案 2 :(得分:0)
开始:
为什么通常不总是总和== foldl1(+)?
foldl1 (+) []
*** Exception: Prelude.foldl1: empty list
sum []
0
这是主要区别,sum允许与[]
和foldl1 (+)
匹配的模式。
foldl1
本可以与非空列表一起使用,因为有些函数对空列表没有意义,例如:
foldl1 max
max
中的[]
是什么?没有最大值,因为没有元素。
foldl1 max [5,4,2,3]
5