区块链技术博客
www.b2bchain.cn

关于FunctionBuilder求职学习资料

本文介绍了关于FunctionBuilder求职学习资料,有助于帮助完成毕业设计以及求职,是一篇很好的资料。

对技术面试,学习经验等有一些体会,在此分享。

Swift是一门多范式编程语言,自2014年发布至今,经过6年的不断迭代,版本号目前也更新到了5.3Swift5是个大更新,这个版本实现了ABI稳定,这意味着以后在使用Swift5以上的编译器,编译出来的二进制包,就不用在App中附带SwiftRuntime包了,同时Apple也发布了一些Swift Only的framework,相信以后越来越多的库都是SwiftOnly的。而本文的所说的 FunctionBuilder也是第一次做为语言特性在Swift5.1中引入的。长话短说,下面我们来看看:

  1. 什么是FunctionBuilder
  2. FunctionBuilder是怎么工作的
  3. 如何去自定义FunctionBuilder

示例

import SwiftUI  struct ContentView: View {     var body: some View {         VStack(alignment: .leading) {             Text("Turtle Rock")                 .font(.title)             HStack {                 Text("Joshua Tree National Park")                     .font(.subheadline)                 Spacer()                 Text("California")                     .font(.subheadline)             }         }         .padding()     } }

上面的代码是Apple的官方示例代码,如果你了解SwiftUI的话,那你很容易看懂这段代码的作用。看到这段代码的时候,首先让我疑惑的是,VStack中的Block里面的代码,为什么没有return关键字?其次是VStack中的Text、HStack为什么能够被编译器识别,如果随便声明一个变量,能够编译正确吗?

HStack {     Text("hello")     let label = Text("world")     Text("!") }

试试看,这段代码能够正常编译么?

Return-less single expression

之所以在上面的代码中,有看到return关键字是因为Return-less single expression(无返回单表达式),这是Swift的一个新特性。从名字就可以理解,在函数内部仅有一个表达式的时候,可以省略掉return关键字。 这个特性其实在很久之前就有了,你肯定见过以下的代码:

[1,2,3,4,6,7].map{$0 * 2}

在之前的Swift版本中,如果block中的代码只有一个表达式的时候,可以省略掉return。 所以这个特性只是把条件放宽到funtion中。

这个功能实现起来其实很简单,就是编译器在检测到代码中的函数仅有一个statement时候,会在语法树中插入一个叫做return的token。具体的代码你可以点击这里。

auto RS = new (Context) ReturnStmt(SourceLoc(), E); BS->setElement(0, RS); AFD->setHasSingleExpressionBody(); AFD->setSingleExpressionBody(E);

FunctionBuilder

简单来说,FunctionBuilder是允许某些函数(经过注解,通常是通过上下文)从一系列组件中隐式的构建一个值的新特性。比如:

 func build() -> (Int, Int, Int) {   1   2   3 }  //这段代码的解释就像下面这段代码 func build() -> (Int, Int, Int) {   let _a = TupleBuilder.buildExpression(1)   let _b = TupleBuilder.buildExpression(2)   let _c = TupleBuilder.buildExpression(3)   return TupleBuilder.buildBlock(_a, _b, _c) }

在上面的代码中,@TupleBuilder是一个function builder type,也就是说这个函数被用作一个中嵌入式的DSL,从函数的表达式语句中收集部分结果,然后将其组合成返回值。本例中就可以认为 1 2 3 是作为一种特殊的DSL嵌入到build函数内部的,每一行表达式都是单个Int值,build函数最终会返回由这些值组合成的tuple。

其实对于functionbuilder比较难以理解的一点是,嵌入式的DSL 与Swift本身的语言没有一个明确的分界点,以上例来说,如果写成以下方式可能会更容易理解:

func build()-> (Int, Int, Int) {        1     2     3   } }

以这种明确的@{}方式,做为function builder的一个边界,@{}本身是遵守Swift语法的, @{} 内部就是规定的DSL,而外部的代码依然是Swift的标准代码,这样更容易理解。

function builder transform

对于上面build函数的函数体内部代码,需要经过变换成swift的原生代码,才能被正常的去执行。这个过程是由编译器完成的,在语法分析阶段,编译器会将function builder transform应用到已经解析的AST上。另一种情况是用于闭包参数,编译器会重写这个闭包转传成原生的swift代码。 由于在声明中可能是嵌套的语句,所以这个转换也会递归的去转换语句块。

对于普通的语句块来说,每个语句会被转化成一个语句序列,然后串联起来。每个序列可以有选择的生成一个稍后会用到的临时变量值。在对所有的语句进行转换后,会调用buildBlock()函数以生成最终的值。

对于声明的语句这部分不进行任何处理,由开发人员决定

对于不是赋值的表达式语句,如果function builder实现了buildExpression方法,该表达式语句会被当作参数传递给这个方法,调用以后会被用作表达式语句。buildExpression可以被重载来处理不同情况下的expression。

如果是赋值的表达式,处理流程跟上面保持一致,只是总是返回()

static func buildExpression(_ :()) -> Componenet {...}

如果是控制流程语句,比如 if 、if else、 switch(最新的版本支持该语句)等,均会转换成相对应的function builder 方法。对于if 语句,会做如下转换

let value = BuilderType.buildOptional(case0)

对于if -else 语句 或者 Switch语句,编译器会分析该语句的子块,已确定可以产生结果的case数N,该实现是可以嵌套的。然后产生一个平衡二叉树注入到Block中的部分结果中,如下所示

if case0 {   value = BuilderType.buildEither(first: case0) } else if case1 {   value = BuilderType.buildEither(second: BuilderType.buildEither(first: case1)) }

测试

从上面分析来看,很容易得出一个结论,FunctionBuilder对与 UI 或者是Json 这种结构化的数据结构是很友好的。网上已经有很多例子了

  1. 自定义HTML。
  2. 自定义request
  3. 自定义AttributedString

下面提供一个示例展示如何自定义一个functionBuilder。

enum Either<T,U> {   case first(T)   case second(U) }   struct Builder{   static func buildBlock<T1,T2>(_ t1: T1, _ t2: T2) -> (T1,T2) {     return(t1,t2)   }   static func buildBlock<T1,T2,T3>(_ t1: T1, _ t2: T2,_ t3: T3) -> (T1,T2,T3) {     return (t1,t2,t3)   }   static func buildBlock<T1, T2, T3, T4>(_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4)       -> (T1, T2, T3, T4) {     return (t1, t2, t3, t4)   }   static func buildDo<T>(_ value: T) -> T { return value }   static func buildIf<T>(_ value: T?) -> T? { return value }    static func buildEither<T,U>(first value: T) -> Either<T,U> {     return .first(value)   }   static func buildEither<T,U>(second value: U) -> Either<T,U> {     return .second(value)   } }  func tuplify<T>(_ cond: Bool,  body: (Bool) -> T) {   print(body(cond)) }
let name = "dsl" tuplify(true) {   17   3.14159   "Hello, (name.map { $0.uppercased() }.joined())"   do {     ["nested", "do"]     1 + 2 + 3   }   if $0 {     2.71828     ["if", "stmt"]   } }

Swift是一门多范式编程语言,自2014年发布至今,经过6年的不断迭代,版本号目前也更新到了5.3Swift5是个大更新,这个版本实现了ABI稳定,这意味着以后在使用Swift5以上的编译器,编译出来的二进制包,就不用在App中附带SwiftRuntime包了,同时Apple也发布了一些Swift Only的framework,相信以后越来越多的库都是SwiftOnly的。而本文的所说的 FunctionBuilder也是第一次做为语言特性在Swift5.1中引入的。长话短说,下面我们来看看:

  1. 什么是FunctionBuilder
  2. FunctionBuilder是怎么工作的
  3. 如何去自定义FunctionBuilder

示例

import SwiftUI  struct ContentView: View {     var body: some View {         VStack(alignment: .leading) {             Text("Turtle Rock")                 .font(.title)             HStack {                 Text("Joshua Tree National Park")                     .font(.subheadline)                 Spacer()                 Text("California")                     .font(.subheadline)             }         }         .padding()     } }

上面的代码是Apple的官方示例代码,如果你了解SwiftUI的话,那你很容易看懂这段代码的作用。看到这段代码的时候,首先让我疑惑的是,VStack中的Block里面的代码,为什么没有return关键字?其次是VStack中的Text、HStack为什么能够被编译器识别,如果随便声明一个变量,能够编译正确吗?

HStack {     Text("hello")     let label = Text("world")     Text("!") }

试试看,这段代码能够正常编译么?

Return-less single expression

之所以在上面的代码中,有看到return关键字是因为Return-less single expression(无返回单表达式),这是Swift的一个新特性。从名字就可以理解,在函数内部仅有一个表达式的时候,可以省略掉return关键字。 这个特性其实在很久之前就有了,你肯定见过以下的代码:

[1,2,3,4,6,7].map{$0 * 2}

在之前的Swift版本中,如果block中的代码只有一个表达式的时候,可以省略掉return。 所以这个特性只是把条件放宽到funtion中。

这个功能实现起来其实很简单,就是编译器在检测到代码中的函数仅有一个statement时候,会在语法树中插入一个叫做return的token。具体的代码你可以点击这里。

auto RS = new (Context) ReturnStmt(SourceLoc(), E); BS->setElement(0, RS); AFD->setHasSingleExpressionBody(); AFD->setSingleExpressionBody(E);

FunctionBuilder

简单来说,FunctionBuilder是允许某些函数(经过注解,通常是通过上下文)从一系列组件中隐式的构建一个值的新特性。比如:

 func build() -> (Int, Int, Int) {   1   2   3 }  //这段代码的解释就像下面这段代码 func build() -> (Int, Int, Int) {   let _a = TupleBuilder.buildExpression(1)   let _b = TupleBuilder.buildExpression(2)   let _c = TupleBuilder.buildExpression(3)   return TupleBuilder.buildBlock(_a, _b, _c) }

在上面的代码中,@TupleBuilder是一个function builder type,也就是说这个函数被用作一个中嵌入式的DSL,从函数的表达式语句中收集部分结果,然后将其组合成返回值。本例中就可以认为 1 2 3 是作为一种特殊的DSL嵌入到build函数内部的,每一行表达式都是单个Int值,build函数最终会返回由这些值组合成的tuple。

其实对于functionbuilder比较难以理解的一点是,嵌入式的DSL 与Swift本身的语言没有一个明确的分界点,以上例来说,如果写成以下方式可能会更容易理解:

func build()-> (Int, Int, Int) {        1     2     3   } }

以这种明确的@{}方式,做为function builder的一个边界,@{}本身是遵守Swift语法的, @{} 内部就是规定的DSL,而外部的代码依然是Swift的标准代码,这样更容易理解。

function builder transform

对于上面build函数的函数体内部代码,需要经过变换成swift的原生代码,才能被正常的去执行。这个过程是由编译器完成的,在语法分析阶段,编译器会将function builder transform应用到已经解析的AST上。另一种情况是用于闭包参数,编译器会重写这个闭包转传成原生的swift代码。 由于在声明中可能是嵌套的语句,所以这个转换也会递归的去转换语句块。

对于普通的语句块来说,每个语句会被转化成一个语句序列,然后串联起来。每个序列可以有选择的生成一个稍后会用到的临时变量值。在对所有的语句进行转换后,会调用buildBlock()函数以生成最终的值。

对于声明的语句这部分不进行任何处理,由开发人员决定

对于不是赋值的表达式语句,如果function builder实现了buildExpression方法,该表达式语句会被当作参数传递给这个方法,调用以后会被用作表达式语句。buildExpression可以被重载来处理不同情况下的expression。

如果是赋值的表达式,处理流程跟上面保持一致,只是总是返回()

static func buildExpression(_ :()) -> Componenet {...}

如果是控制流程语句,比如 if 、if else、 switch(最新的版本支持该语句)等,均会转换成相对应的function builder 方法。对于if 语句,会做如下转换

let value = BuilderType.buildOptional(case0)

对于if -else 语句 或者 Switch语句,编译器会分析该语句的子块,已确定可以产生结果的case数N,该实现是可以嵌套的。然后产生一个平衡二叉树注入到Block中的部分结果中,如下所示

if case0 {   value = BuilderType.buildEither(first: case0) } else if case1 {   value = BuilderType.buildEither(second: BuilderType.buildEither(first: case1)) }

测试

从上面分析来看,很容易得出一个结论,FunctionBuilder对与 UI 或者是Json 这种结构化的数据结构是很友好的。网上已经有很多例子了

  1. 自定义HTML。
  2. 自定义request
  3. 自定义AttributedString

下面提供一个示例展示如何自定义一个functionBuilder。

enum Either<T,U> {   case first(T)   case second(U) }   struct Builder{   static func buildBlock<T1,T2>(_ t1: T1, _ t2: T2) -> (T1,T2) {     return(t1,t2)   }   static func buildBlock<T1,T2,T3>(_ t1: T1, _ t2: T2,_ t3: T3) -> (T1,T2,T3) {     return (t1,t2,t3)   }   static func buildBlock<T1, T2, T3, T4>(_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4)       -> (T1, T2, T3, T4) {     return (t1, t2, t3, t4)   }   static func buildDo<T>(_ value: T) -> T { return value }   static func buildIf<T>(_ value: T?) -> T? { return value }    static func buildEither<T,U>(first value: T) -> Either<T,U> {     return .first(value)   }   static func buildEither<T,U>(second value: U) -> Either<T,U> {     return .second(value)   } }  func tuplify<T>(_ cond: Bool,  body: (Bool) -> T) {   print(body(cond)) }
let name = "dsl" tuplify(true) {   17   3.14159   "Hello, (name.map { $0.uppercased() }.joined())"   do {     ["nested", "do"]     1 + 2 + 3   }   if $0 {     2.71828     ["if", "stmt"]   } }

Swift是一门多范式编程语言,自2014年发布至今,经过6年的不断迭代,版本号目前也更新到了5.3Swift5是个大更新,这个版本实现了ABI稳定,这意味着以后在使用Swift5以上的编译器,编译出来的二进制包,就不用在App中附带SwiftRuntime包了,同时Apple也发布了一些Swift Only的framework,相信以后越来越多的库都是SwiftOnly的。而本文的所说的 FunctionBuilder也是第一次做为语言特性在Swift5.1中引入的。长话短说,下面我们来看看:

  1. 什么是FunctionBuilder
  2. FunctionBuilder是怎么工作的
  3. 如何去自定义FunctionBuilder

示例

import SwiftUI  struct ContentView: View {     var body: some View {         VStack(alignment: .leading) {             Text("Turtle Rock")                 .font(.title)             HStack {                 Text("Joshua Tree National Park")                     .font(.subheadline)                 Spacer()                 Text("California")                     .font(.subheadline)             }         }         .padding()     } }

上面的代码是Apple的官方示例代码,如果你了解SwiftUI的话,那你很容易看懂这段代码的作用。看到这段代码的时候,首先让我疑惑的是,VStack中的Block里面的代码,为什么没有return关键字?其次是VStack中的Text、HStack为什么能够被编译器识别,如果随便声明一个变量,能够编译正确吗?

HStack {     Text("hello")     let label = Text("world")     Text("!") }

试试看,这段代码能够正常编译么?

Return-less single expression

之所以在上面的代码中,有看到return关键字是因为Return-less single expression(无返回单表达式),这是Swift的一个新特性。从名字就可以理解,在函数内部仅有一个表达式的时候,可以省略掉return关键字。 这个特性其实在很久之前就有了,你肯定见过以下的代码:

[1,2,3,4,6,7].map{$0 * 2}

在之前的Swift版本中,如果block中的代码只有一个表达式的时候,可以省略掉return。 所以这个特性只是把条件放宽到funtion中。

这个功能实现起来其实很简单,就是编译器在检测到代码中的函数仅有一个statement时候,会在语法树中插入一个叫做return的token。具体的代码你可以点击这里。

auto RS = new (Context) ReturnStmt(SourceLoc(), E); BS->setElement(0, RS); AFD->setHasSingleExpressionBody(); AFD->setSingleExpressionBody(E);

FunctionBuilder

简单来说,FunctionBuilder是允许某些函数(经过注解,通常是通过上下文)从一系列组件中隐式的构建一个值的新特性。比如:

 func build() -> (Int, Int, Int) {   1   2   3 }  //这段代码的解释就像下面这段代码 func build() -> (Int, Int, Int) {   let _a = TupleBuilder.buildExpression(1)   let _b = TupleBuilder.buildExpression(2)   let _c = TupleBuilder.buildExpression(3)   return TupleBuilder.buildBlock(_a, _b, _c) }

在上面的代码中,@TupleBuilder是一个function builder type,也就是说这个函数被用作一个中嵌入式的DSL,从函数的表达式语句中收集部分结果,然后将其组合成返回值。本例中就可以认为 1 2 3 是作为一种特殊的DSL嵌入到build函数内部的,每一行表达式都是单个Int值,build函数最终会返回由这些值组合成的tuple。

其实对于functionbuilder比较难以理解的一点是,嵌入式的DSL 与Swift本身的语言没有一个明确的分界点,以上例来说,如果写成以下方式可能会更容易理解:

func build()-> (Int, Int, Int) {        1     2     3   } }

以这种明确的@{}方式,做为function builder的一个边界,@{}本身是遵守Swift语法的, @{} 内部就是规定的DSL,而外部的代码依然是Swift的标准代码,这样更容易理解。

function builder transform

对于上面build函数的函数体内部代码,需要经过变换成swift的原生代码,才能被正常的去执行。这个过程是由编译器完成的,在语法分析阶段,编译器会将function builder transform应用到已经解析的AST上。另一种情况是用于闭包参数,编译器会重写这个闭包转传成原生的swift代码。 由于在声明中可能是嵌套的语句,所以这个转换也会递归的去转换语句块。

对于普通的语句块来说,每个语句会被转化成一个语句序列,然后串联起来。每个序列可以有选择的生成一个稍后会用到的临时变量值。在对所有的语句进行转换后,会调用buildBlock()函数以生成最终的值。

对于声明的语句这部分不进行任何处理,由开发人员决定

对于不是赋值的表达式语句,如果function builder实现了buildExpression方法,该表达式语句会被当作参数传递给这个方法,调用以后会被用作表达式语句。buildExpression可以被重载来处理不同情况下的expression。

如果是赋值的表达式,处理流程跟上面保持一致,只是总是返回()

static func buildExpression(_ :()) -> Componenet {...}

如果是控制流程语句,比如 if 、if else、 switch(最新的版本支持该语句)等,均会转换成相对应的function builder 方法。对于if 语句,会做如下转换

let value = BuilderType.buildOptional(case0)

对于if -else 语句 或者 Switch语句,编译器会分析该语句的子块,已确定可以产生结果的case数N,该实现是可以嵌套的。然后产生一个平衡二叉树注入到Block中的部分结果中,如下所示

if case0 {   value = BuilderType.buildEither(first: case0) } else if case1 {   value = BuilderType.buildEither(second: BuilderType.buildEither(first: case1)) }

测试

从上面分析来看,很容易得出一个结论,FunctionBuilder对与 UI 或者是Json 这种结构化的数据结构是很友好的。网上已经有很多例子了

  1. 自定义HTML。
  2. 自定义request
  3. 自定义AttributedString

下面提供一个示例展示如何自定义一个functionBuilder。

enum Either<T,U> {   case first(T)   case second(U) }   struct Builder{   static func buildBlock<T1,T2>(_ t1: T1, _ t2: T2) -> (T1,T2) {     return(t1,t2)   }   static func buildBlock<T1,T2,T3>(_ t1: T1, _ t2: T2,_ t3: T3) -> (T1,T2,T3) {     return (t1,t2,t3)   }   static func buildBlock<T1, T2, T3, T4>(_ t1: T1, _ t2: T2, _ t3: T3, _ t4: T4)       -> (T1, T2, T3, T4) {     return (t1, t2, t3, t4)   }   static func buildDo<T>(_ value: T) -> T { return value }   static func buildIf<T>(_ value: T?) -> T? { return value }    static func buildEither<T,U>(first value: T) -> Either<T,U> {     return .first(value)   }   static func buildEither<T,U>(second value: U) -> Either<T,U> {     return .second(value)   } }  func tuplify<T>(_ cond: Bool,  body: (Bool) -> T) {   print(body(cond)) }
let name = "dsl" tuplify(true) {   17   3.14159   "Hello, (name.map { $0.uppercased() }.joined())"   do {     ["nested", "do"]     1 + 2 + 3   }   if $0 {     2.71828     ["if", "stmt"]   } }

部分转自互联网,侵权删除联系

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » 关于FunctionBuilder求职学习资料
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们