Swift - 将数组的每个元素乘以2

时间:2017-05-25 07:47:40

标签: arrays swift

我想将代码的每个元素乘以2.这是我的代码:

var arr = [5, 77, 34, -22]

for x in arr {

    arr[arr.index(of: x)!] = x * 2

}

print(arr)

这将在控制台上打印以下内容:

[10, 154, 68, -44]

当我向数组添加1时,我有以下代码:

var arr = [5, 77, 34, -22, 1]

for x in arr {

    arr[arr.index(of: x)!] = x * 2

}

print(arr)

正如所料,结果如下:

[10, 154, 68, -44, 2]

但是,当我使用以下代码向数组添加2时:

var arr = [5, 77, 34, -22, 1, 2]

for x in arr {

    arr[arr.index(of: x)!] = x * 2

}

print(arr)

我得到了一个奇怪的结果:

[10, 154, 68, -44, 4, 2]

为什么会这样?我使用Playground来获得所有这些结果。任何解释都会非常感谢,提前谢谢。

4 个答案:

答案 0 :(得分:2)

首先,上面的乘法元素的方法并不是一个好主意。

<强>为什么吗

因为如果重复该数字,您将把乘法运算应用于首先出现的元素。

 var arr = [1, 2] // result will be [4, 2] 

 // This not expected, you need
 [2, 4]

这就是你的情况。这是每次迭代的数组的STDOUT

[5, 77, 34, -22, 1, 2]
[10, 77, 34, -22, 1, 2]
[10, 154, 34, -22, 1, 2]
[10, 154, 68, -22, 1, 2]
[10, 154, 68, -44, 1, 2]
[10, 154, 68, -44, 2, 2]
[10, 154, 68, -44, 4, 2]

在数组的倒数第二个状态中,您可以看到连续两个2,当您调用index: of时,它会返回它按顺序找到的第一个元素的索引。

更好的方法是使用enumrationmap

  for (index, value) in arr.enumerated() {
    arr[index] = value * 2
  }

  // or
  let array = arr.map {$0 * 2}

答案 1 :(得分:1)

var arr: [Int] = [5, 77, 34, -22, 1, 2]

for x in arr {
    let i = arr.index(of: x)!
    print(i)
    arr[i] = x * 2
}

您遇到此问题是因为您在for循环的每次迭代中重新编写arr。 这就是为什么当你进入最后一个迭代步骤(arr.index(of: 2)!)时,你的数组有下一个视图:[10, 154, 68, -44, 2, 2]

根据文档arr.index(of: x)! returns the first index where the specified value appears in the collection.,最后一步i4,而不是5

答案 2 :(得分:1)

您可以在数组上使用for方法,而不是使用map循环。

    var arr = [5, 77, 34, -22, 1, 2]
    arr = arr.map {
        return $0 * 2
    }
    print(arr)

输出:

[10, 154, 68, -44, 2, 4]

有关map方法的更多信息,请参阅:https://developer.apple.com/reference/swift/dictionary/1689397-map

答案 3 :(得分:0)

乘以2(或其幂)的特殊情况的替代方法:按位左移<<

由于整数以二进制格式存储,因此乘以2(二进制的基数)相当于应用于每个数字的单个按位左移<<

var arr = [5, 77, 34, -22, 1]
arr = arr.map{ $0 << 1 }
print(arr) // [10, 154, 68, -44, 2]

我怀疑这种方法比显式乘法2具有更好的性能(如其他答案中所示),但是以可读性/语义/意图为代价。除非您正在使用HPC应用程序,否则后者应该被重视,并且显式乘法是首选。

关于价值溢出的说明

值得注意的是,对于一个太大而无法安全进入乘法运算的值(比如value > Int.max / 2),显式乘以2将产生一个值溢出,如果使用*运算符

var arr = [5, 77, 34, -22, Int.max]
arr = arr.map{ $0 * 2 } // runtime exception

上面的按位左移方法将通过静默丢弃操作期间发生的任何溢出来避免运行时异常

var arr = [5, 77, 34, -22, Int.max]
arr = arr.map{ $0 << 1 }
print(arr) // [10, 154, 68, -44, -2]

使用掩蔽乘法运算符(&*)进行乘法时也使用相同的丢弃:

var arr = [5, 77, 34, -22, Int.max]
arr = arr.map{ $0 &* 2 }
print(arr) // [10, 154, 68, -44, -2]

要理解按位截断/溢出丢弃在值为-2的情况下如何将签名类型作为Int进行处理,则可以更轻松地研究较小的类型,例如Int8在这种情况下,丢弃溢出变得很明显。

var arr: [Int8] = [5, 77, 34, -22, Int8.max] // allowed values: -128...127
arr = arr.map{ $0 << 1 }
print(arr) // [10, 154, 68, -44, -2]
    // Int8.max      = 127,  0b1111_1111 (unsigned)
    // Int8.min      = -128, 0b0000_0000 (unsigned)
    // Int8.max << 2 = 0b1111_1111 << 1 = { discard overflow } 
    //               = 0b1111_1110 = -2 (unsigned)

print(Int8(bitPattern: 0b1111_1110)) // -2