我一直在尝试创建一个返回给定矩阵的函数,并添加一行&列,其数字是旁边的行/列中的数字的总和 数字是列中数字的总和......
E.g。
(1 2
3 4)
到
(1 2 3
3 4 7
4 6 10)
我的第一个想法是
> extend1 :: Int -> Array (Int,Int) Int
> extend1 ((a,b),(c,d)) = n where
> n = array ((a,b),(c,d),(n,n))
因为我必须使用“数组”。现在我不知道该怎么做。是好还是完全错了?
答案 0 :(得分:4)
使用你对元组的表示而不用考虑一般性,这里是你问题的解决方案。
type Square2 a = ((a,a), (a,a))
type Square3 a = ((a,a,a), (a,a,a), (a,a,a))
extend1 :: Num a => Square2 a -> Square3 a
extend1 ((a,b), (c,d)) =
( (a, b, ab )
, (c, d, cd )
, (ac, bd, abcd) )
where
ab = a + b
cd = c + d
ac = a + c
bd = b + d
abcd = ab + cd
注意我如何使用从输入模式和where
子句中的定义中剔除的变量定义。还要注意我在构建一个全新的输出时如何销毁和消耗输入---一些元素被重用,但结构本身被破坏了。最后请注意我如何选择元组内部的类型为常量并受Num
类型类约束,这允许我使用(+)
进行添加。
类似的函数,通用和使用Array
的类型
extend1A :: Num a => Array (Int,Int) a -> Array (Int, Int) a
我们已经失去了知道Array
的确切大小的能力,但它表明我们的函数需要一个Array
的某个大小,并返回另一个被索引的Array
相同且包含相同的Num
- 约束类型。
array
函数将第一个参数作为一组边界。我们需要根据输入数组的bounds
更新它们
extend1A ary0 = array bounds1 ... where
((minX, minY), (maxX, maxY)) = bounds ary0
(maxX1, maxY1) = (succ maxX, succ maxY)
bounds1 = ((minX, minY, (maxX1, maxY1))
然后array
将“assocs”列表作为其第二个参数。我们可以将任何Array ix a
视为等效(粗略)为关联列表[(ix, a)]
,其列出的值表示“在索引ix
,值为a
”。为此,我们必须知道我们之前管理的bounds
类型的ix
。
要使用旧数据中的信息更新数组,我们可以修改旧数组的assocs
以包含新信息。具体来说,这意味着extend1A
看起来有点像
extend1A ary0 = array bounds1 (priorInformation ++ extraInformation) where
priorInformation = assocs ary0
extraInformation = ...
((minX, minY), (maxX, maxY)) = bounds ary0
(maxX1, maxY1) = (succ maxX, succ maxY)
bounds1 = ((minX, minY, (maxX1, maxY1))
如果extraInformation
为空([]
),则extend1A ary
在输入ary
和{{1}范围内的所有指标上等于ary
在其范围之外的所有范围内。我们需要在undefined
中填写总和信息。
extraInformation
如果我们考虑将数组扩展为三个部分,extend1A ary0 = array bounds1 (priorInformation ++ extraInformation) where
priorInformation = assocs ary0
extraInformation = xExtension ++ yExtension ++ totalExtension
xExtension = ...
yExtension = ...
totalExtension = ...
((minX, minY), (maxX, maxY)) = bounds ary0
(maxX1, maxY1) = (succ maxX, succ maxY)
bounds1 = ((minX, minY, (maxX1, maxY1))
标记为xExtension
,ab
标记为cd
,extend1
标记为{{ {}}中的{}}和yExtension
以及ac
中bd
标记的extend1
我们可以依次计算每个部分。
totalExtension
最简单 - 它只是abcd
中每个extend1
对的“值组件”的总和。它也可以是totalExtension
或(i,a)
的“价值成分”的总和,但为了尽可能明显正确,我们将选择第一个选择并安装在较低位置 - 角落。
priorInformation
请注意,我们可以使用xExtension
子句定义新的函数,例如yExtension
,这些函数会反复出现。
然后我们可以将扩展计算为extend1A ary0 = array bounds1 (priorInformation ++ extraInformation) where
priorInformation = assocs ary0
extraInformation = xExtension ++ yExtension ++ totalExtension
sumValues asscs = sum (map snd asscs)
xExtension = ...
yExtension = ...
totalExtension = [((maxX1, maxY1), sumValues priorInformation)]
((minX, minY), (maxX, maxY)) = bounds ary0
(maxX1, maxY1) = (succ maxX, succ maxY)
bounds1 = ((minX, minY), (maxX1, maxY1))
上的列表推导。我们需要在旧的关联上收集一种特定的总和 - 对一个索引固定的所有值进行求和。
where
然后我们完成了。它效率不高,但所有部分都在一起工作。
sumValues
答案 1 :(得分:2)
使用hmatrix:
import Numeric.LinearAlgebra
import Numeric.LinearAlgebra.Util(col,row)
m = (2><2) [1 , 2
,3 , 4] :: Matrix Double
extend m = fromBlocks [[m ,sr]
,[sc, s]]
where
sr = col $ map sumElements (toRows m)
sc = row $ map sumElements (toColumns m)
s = scalar (sumElements sr)
main = do
print m
print $ extend m
(2><2)
[ 1.0, 2.0
, 3.0, 4.0 ]
(3><3)
[ 1.0, 2.0, 3.0
, 3.0, 4.0, 7.0
, 4.0, 6.0, 10.0 ]
答案 2 :(得分:1)
首先,您应该了解如何与haskell数组进行交互。 Array
数据类型位于Data.Array
中,因此有关详细信息,请查看该模块的文档。
请注意,我省略了您在所有这些函数中找到的Ix i
约束。这种情况并不重要。
bounds :: Array i e -> (i, i)
:此函数返回数组的最小和最大索引。对于1D阵列,这些只是数字。对于2D阵列,它是左上角和右下角(对于矩阵)。
array :: (i, i) -> [(i, e)] -> Array i e
:此函数从边界的最小/最大对创建数组,以及关联列表;也就是说,从索引到值的映射。您的初始示例可以写为array ((0,0),(1,1)) [((0,0),1),((0,1),2),((1,0),3),((1,1),4)]
assocs :: Array i e -> [(i, e)]
:这是array
的“对立面”。所以,arr == array (bounds arr) (assocs arr)
。
现在功能:
extendArray :: Num e => Array (Int, Int) e -> Array (Int, Int) e
extendArray arr =
let
arr' = assocs arr
((xMin, yMin), (xMax, yMax)) = bounds arr
newCol = [ ((n, yMax + 1) , sum [ v | ((x,_),v) <- arr', x == n] ) | n <- [xMin .. xMax]]
newRow = [ ((xMax + 1, n) , sum [ v | ((_,y),v) <- arr', y == n] ) | n <- [yMin .. yMax]]
newCorner = [((xMax + 1, yMax + 1), sum $ map snd arr')]
newArr = array ((xMin, yMin), (xMax + 1, yMax + 1)) (arr' ++ newCol ++ newRow ++ newCorner)
in newArr
让我们分解这里发生的事情。 let
语句中的前两行现在应该是不言自明的。第三行newCol
是神奇发生的地方。它使用列表推导,因此如果您不熟悉它们,请参阅here。
n <- [xMin .. xMax]
:此部分获取所有可能的x值。
[ v | ((x,_),v) <- arr', x == n]
:对于列表arr'
中的所有值,读取为:索引的x坐标等于n,返回该坐标处的值。
((n, yMax + 1) , sum ...
创建一个新索引,其列为1加上旧数组的最大值(因此旧矩阵右侧为1列),其行为n
,其值为上一行的总和(即 - 行n
中所有值的总和)。
newRow
的工作方式相同,但行和列相反。
newCorner
获取矩阵中的所有值,计算它们的总和,并将此值放在新数组中的相应索引处。
newArr
只是将所有步骤组合成一个新数组。显然,每个方向的界限都会增加一个。新数组的关联将包括所有旧关联,以及我们计算的新关联。
所以:
>let x = array ((0,0),(1,1)) [((0,0),1),((0,1),2),((1,0),3),((1,1),4)]
>x
array ((0,0),(1,1)) [((0,0),1),((0,1),2),((1,0),3),((1,1),4)]
>extendArray x
array ((0,0),(2,2)) [((0,0),1),((0,1),2),((0,2),3),((1,0),3),((1,1),4),((1,2),7),((2,0),4),((2,1),6),((2,2),10)]
请注意,此实现效率不高(但很简单)。在newRow
中,我们遍历整个矩阵xMax - xMin
次(每个n
值一次)。由于assocs
始终以相同的顺序(从左到右的行,从上到下的列)返回元素,因此最好将列表arr'
拆分为每个长度为yMax - yMin
的列表;这会给我们一个行列表。但我会把这个优化留给你。