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: "")} 
}