在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
}
在这里,我们实现了CustomStringConvertible的description属性,该属性在每次打印对象时都会调用:
让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
}