在Swift 3.0中创建矩阵类

(在GitHub上构建Swift 3.0)

Swift公开了图形程序员从C ++等语言中缺少的一些广受欢迎的功能,在C ++中,大多数游戏引擎都是在其中编程的。游戏引擎当然会大量使用矩阵,因此我们全面介绍了Swift。

矩阵是具有表格数据的对象,因此就属性而言,我们有两个主要方面:数据和维度,如宽度和高度。 我们将从创建一个实现CustomStringConvertible的新类开始。 这是允许对象成为对象的公共协议。

 类矩阵:CustomStringConvertible { 

内部var数据:Array

var行:Int
var栏:Int

init(_ data:Array ,rows:Int,columns:Int){
self.data =数据
self.rows =行
self.columns =列
}

init(rows:Int,column:Int){
self.data = [Double](重复:0.0,计数:行*列)
self.rows =行
self.columns =列
}

}

存取资料

我们传递的数组是一维的,我们可以假设有一个二维数组,其中将包含行和列属性作为其结构的一部分。 但是,在对此进行编码时,我发现仅传递标准数组并分别指示矩阵的形状会更容易。 这也是像numpy这样写得很好的库当前正在使用的方法。 另一个初始化程序将创建一个充满0.0值的矩阵。

 下标(row:Int,col:Int)-> Double { 
得到{
返回数据[(行*列)+ col]
}

设置{
self.data [(行*列)+ col] = newValue
}
}

将其数据保存在一维数组中意味着我们需要手动计算索引,但这是一个很小的代价,因为我们只需要执行一次即可。 之后,我们可以按以下方式访问数据:

 让m = Matrix([0.5,3.0] rows:1,columns:2)//创建 
let v = m [0,1] // v = 0.3(第一行的第二个元素)

列印

现在,我们绝对希望可以随时在终端上查看打印的数据,并且我们也拥有执行此操作的工具!

 覆盖var说明:字符串{ 
var dsc =“”
对于0中的行。<行{
for col in 0 .. <columns {
让s = String(self [row,col])
dsc + = s +“”
}
dsc + =“ \ n”
}
返回dsc
}

在这里,我们实现了CustomStringConvertibledescription属性,该属性在每次打印对象时都会调用:

 让m = Matrix([0.5,3.0] rows:1,columns:2)//创建 
print(“我的矩阵是:\(m)”)

复制中

另一个实用程序功能应该是复制功能。

  func copy(with zone:NSZone?= nil)->矩阵{ 
让cp = Matrix(self.data,行:self.rows,列:self.columns)
返回cp
}

运作方式

现在我们来了解事物! 由于矩阵是数学对象,因此我们希望在表达式(如数字)中使用它们。 因此,我们将重载+*之类的标准运算符,以允许这种操作。

加成

  func +(左:矩阵,右:矩阵)->矩阵{ 

前提条件(left.rows == right.rows && left.columns == right.columns)
令m = Matrix(left.data,行:left.rows,列:left.columns)
用于0 .. <left.rows {
代表col in 0 .. <left.columns {
m [row,col] + =正确的[row,col]
}
}
返回m
}

作为优秀的开发人员,我们也在为我们的项目编写测试。 为了测试加法,我们编写:

  func testAddition(){ 
让sum = Matrix([2,2],行:1,列:2)+ Matrix([1,1],行:1,列:2)
XCTAssert(sum == Matrix([3,3],行:1,列:2),“ 1x1矩阵错误”)
}

我们可以对标量值的减法和乘法进行相同的操作。 我们需要做的就是将+ =更改为-=* =

平等

您可能还记得覆盖了Objective-C中的isEqual:函数。 在这里它变得更加直观,因为我们也可以将==运算符用于自定义类型。

  func ==(left:Matrix,right:Matrix)-> Bool { 
如果left.rows!= right.rows {
返回假
}
如果left.columns!= right.columns {
返回假
}
对于i in 0 .. <left.rows {
对于0中的j .. <left.columns {
如果left [i,j]!= right [i,j] {
返回假
}
}
}
返回真
}

为了使2个矩阵相等,所有元素必须一一相等。

换位

我们可以在矩阵上执行的特殊操作是转置。 这实际上将翻转矩阵,使其行成为列,反之亦然。 为此,我们将引入一个新的操作数。 遵循矩阵的数学表达式很像。 在这里,我选择了插入符号“ ^”,但任何人都可以。

 后缀运算符^ 
后缀函数^(m:Matrix)->矩阵{
令t = Matrix(行:m。列,列:m.rows)
用于0 .. <m.rows {
for col in 0 .. <m.columns {
t [col,row] = m [row,col]
}
}
返回t
}

我们用以下方法进行测试:

  func testTransposition(){ 
var trans = Matrix([1,2,3,4,5,6],行数:3,列数:2)
XCTAssert(trans ^ == Matrix([1、3、5、2、4、6],行:2,列:3),“矩阵未正确转置”)
}

乘法

现在为棘手的人。 矩阵乘法。 我们通过上面两个人口的例子看到了它是如何工作的。

  func *(左:矩阵,右:矩阵)->矩阵{ 

var lcp = left.copy()
var rcp = right.copy()

if(lcp.rows == 1 && rcp.rows == 1)&&(lcp.columns == rcp.columns){//单行矩阵的异常(受numpy启发)
rcp = rcp ^
}
else if(lcp.columns == 1 && rcp.columns == 1)&&(lcp.rows == rcp.rows){//单行矩阵的异常(受numpy启发)
lcp = lcp ^
}

前提条件(lcp.columns == rcp.rows,“矩阵不能相乘”)

让点=矩阵(行:lcp。行,列:rcp。列)

对于i in 0 .. <lcp.rows {
对于0中的j .. <rcp.columns {
让a = lcp.row(index:i)** rcp.col(index:j)
点[i,j] = a
}
}
返回点
}

要乘以矩阵,第一个的行必须等于第二个的列。 但是,在上面的代码中,我们通过转置具有单行或单列的矩阵来代表用户保留某些情况。 我的理由是,很多时候我会使用矩阵作为数组,原因是我可能希望它们稍后与矩阵进行交互。

您会注意到正在使用**的新运算符。 这是我对两个向量的内积的解释。 将一个数组(向量)的所有元素与另一个大小相等的数组相乘,并将结果求和成一个值。

 中缀运算符** 
func **(左:[Double],右:[Double])-> Double {
var d:Double = 0
对于0中的i .. <left.count {
d + =左[i] *右[i]
}
返回d
}