Groovy 乐高积木™
发布时间:2023-04-25 11:28PM (最后更新:2023-04-27 10:42PM)
Donald Raab 继续了他的有趣的学习 Eclipse Collections 系列。他最新的博文,第 4 部分,着眼于处理集合中的信息。
基本集合处理
Donald 有一个有用的基本集合处理操作比较表。我们将添加一个 Groovy 列
操作 | Eclipse Collections | Java Streams | Groovy |
---|---|---|---|
执行 |
|
|
|
过滤 |
|
|
|
转换 |
|
|
|
查找 |
|
|
|
测试 |
|
|
|
计数 |
|
|
|
我们强烈建议阅读 Donald 的博文,以了解这些类别的更多背景信息。
另请注意,虽然 Groovy 具有内置的集合处理能力(第三列),但它也与 Eclipse Collections 和 Java Streams 配合得很好。因此,在使用 Groovy 时,前两列也同样有效。
我们的示例领域
我们将遵循 Donald 博文中的一个乐高积木示例。
为了我们的目的,我们将稍微简化示例,并且在这篇博文中忽略不同类型的块。
因此,我们将从一个颜色枚举开始。我们只对表示简单的块感兴趣,并使用彩色点来表示块的顶视图
enum Color {
RED("🔴"),
YELLOW("🟡"),
BLUE("🔵"),
GREEN("🟢"),
WHITE("⚪️"),
BLACK("⚫️")
final String circle
Color(String circle) {
this.circle = circle
}
}
我们将有一个类似于 Donald 博文的尺寸记录(但包含 toString()
)
record Dimensions(int width, int length) {
String toString() { "$length X $width" }
}
现在,我们的乐高积木记录只是结合了颜色和尺寸
record LegoBrick(Color color, Dimensions dimensions) {
LegoBrick(Color color, int width, int length) {
this(color, new Dimensions(width, length))
}
static generateMultipleSizedBricks(int count, Set<Color> colors, Set<Dimensions> sizes) {
[[colors, sizes].combinations() * count]*.collect{
Color c, Dimensions d -> new LegoBrick(c, d)
}.sum()
}
String toString() {
([color.circle * dimensions.length] * dimensions.width).join('\n')
}
int length() {
dimensions.length()
}
int width() {
dimensions.width()
}
}
虽然我们在这篇博文中没有使用它,但我们创建了一个额外的构造函数,以便更容易创建特定尺寸的积木。还有一个工厂方法用于组合积木集合。
一些可玩的积木
我们在测试脚本中做的第一件事是设置一些积木以用于其余示例
Set sizes = [[1, 2], [2, 2], [1, 3], [2, 3], [2, 4]].collect {
h, w -> new Dimensions(h, w)
}
Set colors = Color.values()
var bricks = LegoBrick.generateMultipleSizedBricks(5, colors, sizes)
assert bricks.size() == 150
积木的类型由其颜色和尺寸决定。有五种不同的尺寸和六种颜色。集合中每种类型有五个。总共有 150 块积木。
使用 each
(“执行”)
Set seen = []
bricks.shuffled().each {
if (seen.add(it.dimensions)) {
println "$it ($it.dimensions)"
}
}
我们将积木打乱并逐一处理。如果我们看到一个以前未见过尺寸的积木,我们会输出它及其尺寸。
输出将类似于此
🔴🔴 (2 X 1) 🔵🔵🔵 (3 X 1) ⚫️⚫️⚫️ ⚫️⚫️⚫️ (3 X 2) 🔴🔴 🔴🔴 (2 X 2) ⚪️⚪️⚪️⚪️ ⚪️⚪️⚪️⚪️ (4 X 2)
由于洗牌,您可能会看到不同的颜色或不同顺序的尺寸。
使用 findAll
(“过滤”)
现在让我们找出宽度为二的红色积木的独特尺寸(我们将按长度排序)
var redWidthTwo = bricks.findAll(b -> b.width() == 2 && b.color == RED)
.toSet()
.sort(LegoBrick::length)
assert redWidthTwo.join(',\n') == '''\
🔴🔴
🔴🔴,
🔴🔴🔴
🔴🔴🔴,
🔴🔴🔴🔴
🔴🔴🔴🔴'''
使用 split
(也是“过滤”)
让我们找出长度为 4 或更多的积木(我们将只找出独特的变体并按颜色排序)
def (selected, rejected) = bricks.findAll(b -> b.length() > 3)
.toSet()
.sort(LegoBrick::color)
.split { b ->
switch (b.color) {
case GREEN, WHITE, YELLOW -> true
case BLUE, RED, BLACK -> false
}
}
assert selected.join(',\n') == '''
🟡🟡🟡🟡
🟡🟡🟡🟡,
🟢🟢🟢🟢
🟢🟢🟢🟢,
⚪️⚪️⚪️⚪️
⚪️⚪️⚪️⚪️
'''.stripIndent().trim()
assert rejected.join(',\n') == '''
🔴🔴🔴🔴
🔴🔴🔴🔴,
🔵🔵🔵🔵
🔵🔵🔵🔵,
⚫️⚫️⚫️⚫️
⚫️⚫️⚫️⚫️
'''.stripIndent().trim()
使用 collect
(“转换”)
让我们将每个积木转换为其尺寸的 toString,然后找出唯一值
Set dims = bricks.collect(b -> b.dimensions.toString()).toUnique()
assert dims == ['2 X 1', '2 X 2', '3 X 1', '3 X 2', '4 X 2'] as Set
使用 find
(“查找”)
让我们再次洗牌(这里没有作弊!)然后找到第一个宽度和长度为 2 的绿色积木
var greenTwoByTwo = bricks.shuffled().find {
b -> b.width() == b.length() && b.color == GREEN
}
assert greenTwoByTwo.toString() == '🟢🟢\n🟢🟢'
使用 any
和 every
(“测试”)
让我们检查是否没有 1 x 1(或某种 0 尺寸的积木)。宽度或长度必须严格大于 1。另外,让我们检查是否存在宽度与长度相同的积木(回想我们之前的 greenTwoByTwo
只是一个例子)。
assert bricks.every { b -> b.width() > 1 || b.length() > 1 }
assert bricks.any { b -> b.width() == b.length() }
使用 count
(“计数”)
让我们数一数有多少绿色积木,以及有多少积木的长度为 4
assert bricks.count { b -> b.color == GREEN } == 25
assert bricks.count { b -> b.length() == 4 } == 30
积木马赛克
在我们的最后一个例子中,我们取了一个由 (1 x 1) 及更大尺寸的积木组成的马赛克并将它们组合在一起。我们使用了 toString 并为了节省空间(并带来片刻的悬念)我们将其压缩并以分块的 base64 编码。您的挑战,如果您选择接受,就是从其压缩表示中解码积木马赛克。这里有一些可能有所帮助的代码
var encodedCompressedLegoMosaic = '''
eJztmj1uwzAMhfdcvkunLN3duUDPkwskRwiCoKljm9Tjn0gZBmLCkmmB+h5NyUZOl/P39fdjOJse
gLs9VQhCYW9fnz/pQRw6HDpUsA8R/o5nT1Ywxs7B6M+5v/Mf1O6Af5HMFzVDsU/Eudhu0R4q61+/
IN5rupOFelHe6bApnHrYHOlZXamQw4ylLjkKwMONQl8g6RUSuGBHinc09ir1Zn3iAnqNmKrjdsRt
r/Qs5pspxNv09RRLMJdaNX88s912jcTIyJi853+/eQrTfJBGGVziZ12RHSG+a6TuxasWvoRwPYZh
t/vZUsnlO/3M8/R09ca+UshSkknSCPIL/7nWUMFBRPAtTPhoKSKgU0PXIIEWdCC6LbxlYxSnxguK
VVIdUqMea7J4EcKXCERLPgZ8wcF9pNp3EsMim10wL0+LGPjuLFkMHQ7kKYlCJkx2HyWC9ODp++qx
bSWvetaX67fIFg7Npvv6oOHdIoB/oPD5UkRGvHkL33T80KaDGnkYo8zC2VC5K8JdoETKW2+QlpJf
j6WWxpV6gRMO4j4nJH8DqYLcZOtGlJjGB70HVXmJ62fVsSlYO8NVoMyirA4+k3KF9OwpQzL0PWP2
nz91abD/we3DHmIUsqQYd3YE/rA=
'''.trim()
var os = new ByteArrayOutputStream()
try (var ios = new InflaterOutputStream(os)) {
ios.write(encodedCompressedLegoMosaic.decodeBase64())
}
println os
输出留给读者作为练习(但如果您在终端上尝试,您应该至少有 72 x 36 的布局,并使用支持 unicode 的终端)。
剧透警告:如果您不想自己运行脚本,请在此处尝试。
结论
我们快速了解了 Groovy 的一些基本集合处理功能。我们真的只触及了皮毛。如果您想查看更多方法,请查看早期的一篇博文,其中提供了 Groovy 列表处理备忘单。此外,我们强烈建议您使用 Groovy 尝试 Donald 原始博文中的所有 Eclipse Collections 示例。