操作列表
压缩和展平是两种常见的操作,用于操作
List。
压缩
zip() 通过模仿夹克上的拉链的行为,将两个 List 结合在一起,将相邻的 List 元素成对配对:
// ManipulatingLists/Zipper.kt
import atomictest.eq
fun main() {
val left = listOf("a", "b", "c", "d")
val right = listOf("q", "r", "s", "t")
left.zip(right) eq // [1]
"[(a, q), (b, r), (c, s), (d, t)]"
left.zip(0..4) eq // [2]
"[(a, 0), (b, 1), (c, 2), (d, 3)]"
(10..100).zip(right) eq // [3]
"[(10, q), (11, r), (12, s), (13, t)]"
}
- [1] 将
left与right进行压缩,结果是一个Pair的List,将left中的每个元素与其相应的right中的元素配对。 - [2] 您还可以使用范围来对
zip()进行调用。 - [3] 范围
10..100比right大得多,但在其中一个序列用尽时,压缩过程会停止。
zip() 也可以对其创建的每个 Pair 执行操作:
// ManipulatingLists/ZipAndTransform.kt
package manipulatinglists
import atomictest.eq
data class Person(
val name: String,
val id: Int
)
fun main() {
val names = listOf("Bob", "Jill", "Jim")
val ids = listOf(1731, 9274, 8378)
names.zip(ids) { name, id ->
Person(name, id)
} eq "[Person(name=Bob, id=1731), " +
"Person(name=Jill, id=9274), " +
"Person(name=Jim, id=8378)]"
}
names.zip(ids) { ... } 生成了一个名称 - id Pair 序列,并将 lambda 应用于每个 Pair。结果是初始化的 Person 对象的 List。
要从单个 List 中合并两个相邻的元素,使用 zipWithNext():
// ManipulatingLists/ZippingWithNext.kt
import atomictest.eq
fun main() {
val list = listOf('a', 'b', 'c', 'd')
list.zipWithNext() eq listOf(
Pair('a', 'b'),
Pair('b', 'c'),
Pair('c', 'd'))
list.zipWithNext { a, b -> "$a$b" } eq
"[ab, bc, cd]"
}
对 zipWithNext() 的第二次调用在压缩之后执行了附加操作。
展平
flatten() 接受一个包含元素本身是 List 的元素的 List(即 List 的 List),并将其展平为包含单个元素的 List:
// ManipulatingLists/Flatten.kt
import atomictest.eq
fun main() {
val list = listOf(
listOf(1, 2),
listOf(4, 5),
listOf(7, 8),
)
list.flatten() eq "[1, 2, 4, 5, 7, 8]"
}
flatten() 帮助我们理解另一个在集合上执行的重要操作:flatMap()。让我们产生一个范围的所有可能的 Pair:
// ManipulatingLists/FlattenAndFlatMap.kt
import atomictest.eq
fun main() {
val intRange = 1..3
intRange.map { a -> // [1]
intRange.map { b -> a to b }
} eq "[" +
"[(1, 1), (1, 2), (1, 3)], " +
"[(2,
1), (2, 2), (2, 3)], " +
"[(3, 1), (3, 2), (3, 3)]" +
"]"
intRange.map { a -> // [2]
intRange.map { b -> a to b }
}.flatten() eq "[" +
"(1, 1), (1, 2), (1, 3), " +
"(2, 1), (2, 2), (2, 3), " +
"(3, 1), (3, 2), (3, 3)" +
"]"
intRange.flatMap { a -> // [3]
intRange.map { b -> a to b }
} eq "[" +
"(1, 1), (1, 2), (1, 3), " +
"(2, 1), (2, 2), (2, 3), " +
"(3, 1), (3, 2), (3, 3)" +
"]"
}
每种情况下的 lambda 都是相同的:将每个 intRange 元素与每个 intRange 元素结合,以生成所有可能的 a to b Pair。但是在 [1] 中,map() 有助于保留额外的信息,这里我们已经生成了三个 List,每个都对应于 intRange 中的一个元素。在某些情况下,这些额外的信息是必不可少的,但在这里我们不需要它 - 我们只需要一个单一的扁平 List,其中包含所有组合,没有额外的结构。
有两个选择。[2] 展示了将 flatten() 函数应用于移除这个额外结构并将结果展平为单个 List,这是可接受的方法。但是,这是一个非常常见的任务,Kotlin 提供了一个称为 flatMap() 的组合操作,它通过单个调用执行 map() 和 flatten()。[3] 展示了 flatMap() 的使用。在大多数支持函数式编程的语言中,您会发现 flatMap()。
下面是 flatMap() 的另一个示例:
// ManipulatingLists/WhyFlatMap.kt
package manipulatinglists
import atomictest.eq
class Book(
val title: String,
val authors: List<String>
)
fun main() {
val books = listOf(
Book("1984", listOf("George Orwell")),
Book("Ulysses", listOf("James Joyce"))
)
books.map { it.authors }.flatten() eq
listOf("George Orwell", "James Joyce")
books.flatMap { it.authors } eq
listOf("George Orwell", "James Joyce")
}
我们希望得到一个作者的 List。map() 生成了一个作者的 List 的 List,这并不是很方便。flatten() 将其接受并生成一个简单的 List。flatMap() 在单个步骤中生成相同的结果。
在此示例中,我们使用 map() 和 flatMap() 将 enum Suit 和 Rank 结合在一起,生成一副 Card:
// ManipulatingLists/PlayingCards.kt
package manipulatinglists
import kotlin.random.Random
import atomictest.*
enum class Suit {
Spade, Club, Heart, Diamond
}
enum class Rank(val faceValue: Int) {
Ace(1), Two(2), Three(3), Four(4), Five(5),
Six(6), Seven(7), Eight(8), Nine(9),
Ten(10), Jack(10), Queen(10), King(10)
}
class Card(val rank: Rank, val suit: Suit) {
override fun toString() =
"$rank of ${suit}s"
}
val deck: List<Card> =
Suit.values().flatMap { suit ->
Rank.values().map { rank ->
Card(rank, suit)
}
}
fun main() {
val rand = Random(26)
repeat(7) {
trace("'${deck.random(rand)}'")
}
trace eq """
'Jack of Hearts' 'Four of Hearts'
'Five of Clubs' 'Seven of Clubs'
'Jack of Diamonds' 'Ten of Spades'
'Seven of Spades'
"""
}
在初始化 deck 中,内部的 Rank.values().map 生成了四个 List,一个对应于每个 Suit,因此我们在外部循环上使用了 flatMap(),以生成一个
Card 的 List,用于 deck。
练习和解答可以在 www.AtomicKotlin.com 找到。