在Swift中列表理解
语言指南显示没有列表理解的痕迹。 在Swift中完成这个的最好方法是什么? 我正在寻找类似于:
evens = [ x for x in range(10) if x % 2 == 0]
从Swift 2.x开始,有一些与Python风格的列表理解相当的短语。
Python公式的最直接的修改(读取类似于“将变换应用于受filter限制的序列”)包括链接可用于所有SequenceType
的map
和filter
方法,并从Range
:
// Python: [ x for x in range(10) if x % 2 == 0 ] let evens = (0..<10).filter { $0 % 2 == 0 } // Another example, since the first with 'x for x' doesn't // use the full ability of a list comprehension: // Python: [ x*x for x in range(10) if x % 2 == 0 ] let evenSquared = (0..<10).filter({ $0 % 2 == 0 }).map({ $0 * $0 })
请注意, Range
是抽象的 – 它实际上并不创build你要求的整个值列表,只是一个懒惰地按需提供它的构造。 (在这个意义上,它更像Python的xrange
。)但是, filter
调用返回一个Array
,所以你失去了“懒惰”的方面。 如果你想一直保持collections懒惰,只是这样说:
// Python: [ x for x in range(10) if x % 2 == 0 ] let evens = (0..<10).lazy.filter { $0 % 2 == 0 } // Python: [ x*x for x in range(10) if x % 2 == 0 ] let evenSquared = (0..<10).lazy.filter({ $0 % 2 == 0 }).map({ $0 * $0 })
与Python中的列表理解语法(以及某些其他语言中的类似结构)不同,Swift中的这些操作遵循与其他操作相同的语法。 也就是说,对一系列数字进行构造,过滤和操作的过程与对一组对象进行过滤和操作是相同的语法风格 – 您不必为某种工作使用函数/方法语法并列出另一个的理解语法。
您可以将其他函数传递给filter
和map
调用,并链接其他便利的转换,如sort
和reduce
:
// func isAwesome(person: Person) -> Bool // let people: [Person] let names = people.filter(isAwesome).sort(<).map({ $0.name }) let sum = (0..<10).reduce(0, combine: +)
然而,根据你要做什么,可能有更简洁的方式来expression你的意思。 例如,如果您特别需要偶数列表,则可以使用stride
:
let evenStride = 0.stride(to: 10, by: 2) // or stride(through:by:), to include 10
就像范围一样,这会让你成为一个生成器,所以你需要从中创build一个Array
或者遍历它来查看所有的值:
let evensArray = Array(evenStride) // [0, 2, 4, 6, 8]
编辑:重大修改为Swift 2.x. 如果需要Swift 1.x,请参阅编辑历史logging。
使用Swift 3,根据您的需要或口味,您可以select以下七种Playground代码之一,这些代码相当于Python列表的理解。
#1。 使用stride(from:to:by:)
函数
let sequence = stride(from: 0, to: 10, by: 2) let evens = Array(sequence) // let evens = sequence.map({ $0 }) // also works print(evens) // prints [0, 2, 4, 6, 8]
#2。 使用CountableRange
filter(_:)
方法
let range = 0 ..< 10 let evens = range.filter({ $0 % 2 == 0 }) print(evens) // prints [0, 2, 4, 6, 8]
#3。 使用CountableRange
flatMap(_:)
方法
let range = 0 ..< 10 let evens = range.flatMap({ $0 % 2 == 0 ? $0 : nil }) print(evens) // prints [0, 2, 4, 6, 8]
#4。 使用sequence(first:next:)
function
let unfoldSequence = sequence(first: 0, next: { $0 + 2 < 10 ? $0 + 2 : nil }) let evens = Array(unfoldSequence) // let evens = unfoldSequence.map({ $0 }) // also works print(evens) // prints [0, 2, 4, 6, 8]
#5。 使用AnySequence
init(_:)
初始化器
let anySequence = AnySequence<Int>({ () -> AnyIterator<Int> in var value = 0 return AnyIterator<Int> { defer { value += 2 } return value < 10 ? value : nil } }) let evens = Array(anySequence) // let evens = anySequence.map({ $0 }) // also works print(evens) // prints [0, 2, 4, 6, 8]
#6。 对where子句使用for循环
var evens = [Int]() for value in 0 ..< 10 where value % 2 == 0 { evens.append(value) } print(evens) // prints [0, 2, 4, 6, 8]
#7。 如果条件使用for循环
var evens = [Int]() for value in 0 ..< 10 { if value % 2 == 0 { evens.append(value) } } print(evens) // prints [0, 2, 4, 6, 8]
一般来说,Python中的列表理解可以写成如下格式:
[f(x) for x in xs if g(x)]
这是一样的
map(f, filter(g, xs))
因此,在Swift中你可以把它写成
listComprehension<Y>(xs: [X], f: X -> Y, g: X -> Bool) = map(filter(xs, g), f)
例如:
map(filter(0..<10, { $0 % 2 == 0 }), { $0 })
从Swift 2开始,你可以这样做:
var evens = [Int]() for x in 1..<10 where x % 2 == 0 { evens.append(x) } // or directly filtering Range due to default implementations in protocols (now a method) let evens = (0..<10).filter{ $0 % 2 == 0 }
必须承认,我很惊讶没有人提到flatmap,因为我认为它是最接近Swift必须列出(或设置或dict)理解的东西。
var evens = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].flatMap({num -> Int? in if num % 2 == 0 {return num} else {return nil} })
平面地图需要一个闭包,你可以返回单个值(在这种情况下,它将返回一个包含所有非零值的数组,并丢弃nils)或者返回数组段(在这种情况下,它会将所有的段连接在一起并返回)。
平面地图似乎大多数(总是?)无法推断返回值。 当然,在这种情况下,它不能,所以我将它指定为 – > Int? 这样我才能返回nils,从而抛弃这些奇怪的元素。
如果你喜欢,你可以嵌套平面图。 而且我发现它们比地图和filter的组合更直观(虽然显然也更有限)。 例如,使用平面地图的最佳答案是“均匀平方”
var esquares = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].flatMap({num -> Int? in if num % 2 == 0 {return num * num} else {return nil} })
语法并不像python的那样是一行一行的。 我不确定我是否喜欢那样(因为python中的简单例子很短,而且仍然很可读)或者更多(因为复杂的情况可能疯狂地失去控制,而有经验的Python程序员经常认为它们是完美的当同一家公司的初学者可能需要半个小时的时间来解释它打算做什么,更不用说它实际上在做什么)。
这里是您返回单个项目或零的flatMap的版本, 这里是您返回细分的版本。
这可能也值得查看array.map和array.forEach,因为它们都非常方便。
一种方法是:
var evens: Int[]() for x in 0..<10 { if x%2 == 0 {evens += x} // or evens.append(x) }
- 范围运算符
- 数组