ResultBuilder学习笔记三:支持循环
我们在前面的博文中创建了一个非常简单的结果构建器ConcatBuilder
,用于连接多个字符串,随后在后续博文中对之进行了扩展,使其可以支持自定义的输入数据类型,这个特性非常重要,它是DSL的基础。 这篇博文将继续对ConcatBuilder
进行扩展,这次,我们讨论如何增加对循环的支持。
理解循环
ConcatBuilder
已经可以支持Int
,String
和Star
为输入。只要愿意,还可以增加更多的数据类型。但是,如果要使用它拼接1到10 个整数,该如何呢?,当然可以像下面这样:
@ConcatBuilder var str:String {
1
2
3
4
5
6
7
8
9
10
}
print( str )
但是,100个1000个整数呢,总不至于写100遍1000遍吧。 我们当然希望按下述循环方式:
@ConcatBuilder var str:String {
"春眠不觉晓"
"处处闻啼鸟"
for i in 1...100
{
i
}
}
这时,就需要ResultBuilder能够支持循环了。
支持循环
就像能够轻易支持多种输入数据类型一样,ResultBuilder也可以轻易支持循环, 只需要增加某种类型的buildArray
函数即可。对于我们的场景, 在ConcatBuilder
中像下面这样实现buildArray
函数:
static func buildArray(_ components: [String]) -> String {
return components.joined(separator: "")
}
再次运行,结果如下:
春眠不觉晓处处闻啼鸟123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
没错,就这么简单!可能你已经注意到,为什么上述buildArray
的输入参数不是整数而是字符串类型呢?答案是前面的buildExpression (_ component: Int)
函数,ResultBuilder会使用该函数将for-in
循环中的每个整数首先转换成字符串,最终构成一个临时的字符串数组,然后再调用buildArray
函数输出。这些识别,调度和转换都由ResultBuilder自动完成,我们无需关心。这也正是ResultBuilder设计精妙之处。
由于有了不同的buideExpression()
函数,如下循环代码都可以正常工作:
@ConcatBuilder var str:String {
"春眠不觉晓"
"处处闻啼鸟"
//整数
for i in 1...5
{
i
}
//自定义类型
for i in 1...3
{
Star(length:i)
}
//混合
for i in 1...3
{
i
Star(length:i)
"字符串\(i)"
}
}
ConcatBuilder 完整代码
struct Star
{
let length:Int
func getString()->String
{
return Array(repeating:"*",count:length).joined()
}
}
@resultBuilder
struct ConcatBuilder {
//最终输出
static func buildBlock(_ components: String...) -> String {
return components.joined(separator: "")
}
//支持整数输入
static func buildExpression (_ component: Int) -> String {
return "\(component)"
}
//支持字符串输入
static func buildExpression (_ component: String) -> String {
return component
}
//支持自定义类型输入
static func buildExpression (_ component: Star) -> String {
return component.getString()
}
//支持for-in循环
static func buildArray(_ components: [String]) -> String {
return components.joined(separator: "")
}
}