【发布时间】:2016-03-19 07:54:17
【问题描述】:
在 Python 中,可以有一个列表(类似于 swift 中的数组):
>>> li=[0,1,2,3,4,5]
并对列表的任何/所有执行切片分配:
>>> li[2:]=[99] # note then end index is not needed if you mean 'to the end'
>>> li
[0, 1, 99]
Swift 有一个相似的 切片分配(这是在swift 交互式shell 中):
1> var arr=[0,1,2,3,4,5]
arr: [Int] = 6 values {
[0] = 0
[1] = 1
[2] = 2
[3] = 3
[4] = 4
[5] = 5
}
2> arr[2...arr.endIndex-1]=[99]
3> arr
$R0: [Int] = 3 values {
[0] = 0
[1] = 1
[2] = 99
}
到目前为止,一切都很好。但是,有几个问题。
首先,swift 不适用于空列表或索引在endIndex 之后。如果切片索引在结束索引之后,Python 会追加:
>>> li=[] # empty
>>> li[2:]=[6,7,8]
>>> li
[6, 7, 8]
>>> li=[0,1,2]
>>> li[999:]=[999]
>>> li
[0, 1, 2, 999]
swift中的等价物是错误:
4> var arr=[Int]()
arr: [Int] = 0 values
5> arr[2...arr.endIndex-1]=[99]
fatal error: Can't form Range with end < start
这很容易测试和编码。
第二个问题是杀手:它真的很慢。考虑这个 Python 代码来执行浮点列表的精确求和:
def msum(iterable):
"Full precision summation using multiple floats for intermediate values"
# Rounded x+y stored in hi with the round-off stored in lo. Together
# hi+lo are exactly equal to x+y. The inner loop applies hi/lo summation
# to each partial so that the list of partial sums remains exact.
# Depends on IEEE-754 arithmetic guarantees. See proof of correctness at:
# www-2.cs.cmu.edu/afs/cs/project/quake/public/papers/robust-arithmetic.ps
partials = [] # sorted, non-overlapping partial sums
for x in iterable:
i = 0
for y in partials:
if abs(x) < abs(y):
x, y = y, x
hi = x + y
lo = y - (hi - x)
if lo:
partials[i] = lo
i += 1
x = hi
partials[i:] = [x]
return sum(partials, 0.0)
它通过保持高/低部分求和来工作,以便msum([.1]*10) 准确生成1.0 而不是0.9999999999999999。 msum 的 C 等效项是 Python 中 math library 的一部分。
我试图快速复制:
func msum(it:[Double])->Double {
// Full precision summation using multiple floats for intermediate values
var partials=[Double]()
for var x in it {
var i=0
for var y in partials{
if abs(x) < abs(y){
(x, y)=(y, x)
}
let hi=x+y
let lo=y-(hi-x)
if abs(lo)>0.0 {
partials[i]=lo
i+=1
}
x=hi
}
// slow part trying to replicate Python's slice assignment partials[i:]=[x]
if partials.endIndex>i {
partials[i...partials.endIndex-1]=[x]
}
else {
partials.append(x)
}
}
return partials.reduce(0.0, combine: +)
}
测试功能和速度:
import Foundation
var arr=[Double]()
for _ in 1...1000000 {
arr+=[10, 1e100, 10, -1e100]
}
print(arr.reduce(0, combine: +)) // will be 0.0
var startTime: CFAbsoluteTime!
startTime = CFAbsoluteTimeGetCurrent()
print(msum(arr), arr.count*5) // should be arr.count * 5
print(CFAbsoluteTimeGetCurrent() - startTime)
在我的机器上,这需要 7 秒才能完成。 Python 原生 msum 需要 2.2 秒(大约快 4 倍),库 fsum 函数需要 0.09 秒(快大约 90 倍)
我尝试用arr.removeRange(i..<arr.endIndex) 替换partials[i...partials.endIndex-1]=[x],然后追加。快一点但不多。
问题:
- 这是惯用的swift:
partials[i...partials.endIndex-1]=[x] - 有更快/更好的方法吗?
【问题讨论】:
-
很抱歉问了一个显而易见的问题:您是否在发布配置中编译了 Swift 代码?在我的 MacBook Pro 上大约需要 1 秒。
-
@MartinR:(拍头)——是的,这有很大的不同。现在 0.80 秒或快 10 倍。我对变化的幅度感到惊讶。
-
Swift 4 现在支持部分范围。
array[i...]、array[..<i]和array[...i]函数类似于 Python 的array[i:]、array[:i]和array[:i+1]
标签: python arrays swift performance swift2