ResultBuilder学习笔记三:支持循环
我们在前面的博文中创建了一个非常简单的结果构建器ConcatBuilder
,用于连接多个字符串,随后在后续博文中对之进行了扩展,使其可以支持自定义的输入数据类型,这个特性非常重要,它是DSL的基础。 这篇博文将继续对ConcatBuilder
进行扩展,这次,我们讨论如何增加对循环的支持。
理解循环
ConcatBuilder
已经可以支持Int
,String
和Star
为输入。只要愿意,还可以增加更多的数据类型。但是,如果要使用它拼接1到10 个整数,该如何呢?,当然可以像下面这样:
@ConcatBuilder var str:String { 12345678910 }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{iStar(length:i) "字符串\(i)"}}
ConcatBuilder 完整代码
struct Star{let length:Intfunc getString()->String{return Array(repeating:"*",count:length).joined()}}@resultBuilderstruct 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: "")}
}