在Swift中列出理解

List comprehension in Swift

语言指南没有显示任何列表理解的痕迹。用什么最简洁的方法来迅速完成这项工作?我在找类似的东西:

1
evens = [ x for x in range(10) if x % 2 == 0]


作为SWIFT。北部和东部,有一个短的列表的Python风格的理解。

最简单的适配(python的方程式是什么样的"读应用到一个变换序列受a过滤器"和"map)涉及chaining filter方法可用到所有SequenceTypes,和从Range:a

1
2
3
4
5
6
7
// 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摘要-它并不全是创建list of values,你问它,只是一个简单地构建随需应变供应他们。(这是更多的镰刀状xrangepython)。然而,filterArray呼叫返回,所以你有松散的"懒惰"的方面。如果你想把所有的方式,通过收集的懒惰,只说是这样的:

1
2
3
4
// 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中的列表(和其他一些语言中相似的结构),在后续行动迅速进行相同的语法为其他业务。这是,它的同一风格的语法构成,过滤器,和一系列的经营,因为它是数字滤波器阵列和经营上的对象-你不使用的功能的方法是在一个孩子/句法和语法的工作理解为一个列表。

你的护照和其他功能的filtermap调用链,和其他手机和reducesort变换:

1
2
3
4
5
// func isAwesome(person: Person) -> Bool
// let people: [Person]
let names = people.filter(isAwesome).sort(<).map({ $0.name })

let sum = (0..<10).reduce(0, combine: +)

取决于你走,但有更简明的方式,可以说你的意思。例如,如果你想和偶整数列表,你可以使用stride

1
let evenStride = 0.stride(to: 10, by: 2) // or stride(through:by:), to include 10

在这一类,那么你一发电机,所以你会想让Array从通孔在它或它的iterate湖:所有值

1
let evensArray = Array(evenStride) // [0, 2, 4, 6, 8]

编辑:案例研究迅速修订2。如果你想编辑历史湖1.x.斯威夫特


你可以选择一个敏捷5,一个样品的七阶码在以下游戏的解决你的问题。

# 1。使用stride(from:to:by:)功能

1
2
3
4
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。使用方法Rangefilter(_:)

1
2
3
let range = 0 ..< 10
let evens = range.filter({ $0 % 2 == 0 })
print(evens) // prints [0, 2, 4, 6, 8]

# 3。使用方法RangecompactMap(_:)

1
2
3
let range = 0 ..< 10
let evens = range.compactMap({ $0 % 2 == 0 ? $0 : nil })
print(evens) // prints [0, 2, 4, 6, 8]

# 4。使用sequence(first:next:)功能

1
2
3
4
5
6
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。使用AnySequenceinit(_:)初始化

1
2
3
4
5
6
7
8
9
10
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子句使用环

1
2
3
4
5
var evens = [Int]()
for value in 0 ..< 10 where value % 2 == 0 {
    evens.append(value)
}
print(evens) // prints [0, 2, 4, 6, 8]

# 7。如果使用的是环状态

1
2
3
4
5
6
7
var evens = [Int]()
for value in 0 ..< 10 {
    if value % 2 == 0 {
        evens.append(value)
    }
}
print(evens) // prints [0, 2, 4, 6, 8]


2你可以做为斯威夫特这样的东西:

1
2
3
4
5
6
7
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 }


通常,一个名单,可以理解在Python写的形式:

1
[f(x) for x in xs if g(x)]

这是相同的。

1
map(f, filter(g, xs))

因此,你可以写它在斯威夫特

1
listComprehension<Y>(xs: [X], f: X -> Y, g: X -> Bool) = map(filter(xs, g), f)

例如:

1
map(filter(0..<10, { $0 % 2 == 0 }), { $0 })


不得不承认,我很惊讶没有人提到平面图,因为我认为这是斯威夫特列出(或设置或听写)理解的最接近的事情。

1
2
3
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}
})

flatmap接受一个闭包,您可以返回单个值(在这种情况下,它将返回一个包含所有非nil值的数组并丢弃nil),也可以返回数组段(在这种情况下,它将把您的所有段关联在一起并返回该值)。

平面图似乎主要(总是?)无法推断返回值。当然,在这种情况下它不能,所以我把它指定为->in t?这样我就可以返回nils,从而丢弃奇数元素。

如果您愿意,可以嵌套平面图。我发现它们比地图和过滤器的组合更直观(虽然显然也有点局限)。例如,使用平面图,顶部答案的"Evens Squared"变为,

1
2
3
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程序员经常认为,当同一家公司的初学者需要半个小时来弄清楚什么内容时,它们是完全可读和可维护的。它本来是要做的,更不用说它实际在做什么了。)

这里是从中返回单个项目或零的平面图版本,这里是从中返回段的版本。

可能还值得同时查看array.map和array.foreach,因为它们都非常方便。


这个线程中没有提到的列表理解的一个方面是,您可以将它应用于多个列表的笛卡尔积。python中的示例:

1
[x + y for x in range(1,6) for y in range(3, 6) if x % 2 == 0]

…或者Haskell:

1
[x+y | x <- [1..5], y <- [3..5], x `mod` 2 == 0]

在swift中,2列表等效逻辑是

1
2
3
4
5
6
7
8
9
list0
    .map { e0 in
        list1.map { e1 in
            (e0, e1)
        }
    }
.joined()
.filter(f)
.map(g)

当输入中的列表数量增加时,我们必须增加嵌套级别。

我最近做了一个小图书馆来解决这个问题(如果你认为这是个问题的话)。下面是我的第一个例子,通过图书馆

1
Array(1...5, 3...5, where: { n, _ in n % 2 == 0}) { $0 + $1 }

在一篇博客文章中解释了基本原理(以及更多关于列表理解的内容)。


一个办法是:

1
2
3
4
var evens: Int[]()
for x in 0..<10 {
    if x%2 == 0 {evens += x} // or evens.append(x)
}
  • 远程算子
  • 阵列