Go的功能范例:基本技术



大家好,我们提醒您,本月OTUS中的新课程将在Golang开发人员课程中开始。 尽管讨厌上一篇关于Golang的文章,但我们的自由作家还是决定冒险继续撰写有关该语言的一系列文章。 我们将依靠Golang似乎依赖的功能范式,再次尝试克服困难。



我们提醒您,本文是“课外阅读”的一种材料,与课程计划无关,可在此处找到

显然,其他语言的专业程序员会使用Golang调用
烦恼就像成人的编译语言一样,但是原则上不存在类和继承的概念(尽管OOP是通过一种语言通过结构和接口系统实现的,尽管以一种非常不寻常的方式实现)。 但是,今天,我们在功能范例中研究了熟悉的结构的主要实现,并试图解释它们和语言语法本身。



现在,围绕功能范例(FP)进行了大量宣传。 但是,它也不是解决所有问题的灵丹妙药,也有其优点和缺点。

简要介绍什么是功能范式


功能范例来自数学编程。 它构成了该程序的以下要求:

  • 现有数据无变化。
  • 没有隐藏状态。

这给了我们什么?

我们的职能运作不受第三方影响。 换句话说,该函数应仅返回一个值,并且不应影响任何外部数据。

使用纯功能。 无论输入数据如何,它们都可以提高功能的重新测试可靠性-换句话说,程序对于测试变得更加可靠,其结果也变得更加可预测。

那么,Golang实现功能范例的可能性有哪些:

一流的功能


一流的功能可用于多种编程语言。 本文的读者很可能已经从如此广泛的JavaScript中了解了它们的概念,但是我将再次重复。 第一类的函数(高阶函数)是可以将另一个函数作为知识返回,将一个函数作为自变量并将该函数的值传递给另一个变量的函数。
让我们从一开始就达成共识 :为了节省空间,我删除了此处显示的代码的前两行:'package main'和import'import“ fmt”'。 但是要在您的计算机上运行代码,请记住将其添加)。


func main() { var list = []int{15, 16, 45, 34} //      var out = forEach(list, func(it int) int { //      //forEach   ""  return (it * it) //      }) fmt.Println(out) // [225, 256, 2025, 1156] fmt.Println(list) //      } func forEach(arr []int, fn func(it int) int) []int { //      ,   ,     var newArray = []int{} //     ""   for _, it := range arr { newArray = append(newArray, fn(it)) //      for } return newArray } 


实际上,根本不需要发明自己的map或从头开始map 。 有许多实现此功能的库,仅用于连接它们。 例如, 这个

关闭和计算功能


许多现代编程语言都存在短路。 闭包是引用其父函数的自由作用域变量的函数。 函数递归是函数从形式func(a,b,c)变为形式func(a)(b)(c)

这是Go中的闭包和curring的示例:

 //  func multiply(x int) func(y int) int { //    return func(y int) int { //   ,       JS return x * y } } func main() { //     var mult10 = multiply(10) var mult15 = multiply(15) fmt.Println(mult10(5)) //50 fmt.Println(mult15(15))//225 } 


纯功能


如前所述,纯函数是那些只返回与传入的参数相关联且不影响全局状态的值的函数。

这是一个失败的脏函数示例:

 var arrToSave = map[string]int{} //map -    -   Golang func dirtySum(a, b int) int { c := a + b arrToSave[fmt.Sprintf("%d", a, b)] = c //   ,  "%d" -       return c } 

在这里,我们的职能部门应该接受尽可能可预测的工作:

 func simpleSum(x, y int) int { return x + y } func main() { fmt.Printf("%v", dirtySum(13, 12)) //      //   ""      fmt.Printf("%v", simpleSum(13, 12)) } 

“以某种方式递归进入标准,没有其他人进入标准”
从收集到的有趣笑话开始。

递归


在功能范例中,习惯上优先考虑递归-出于纯度和透明度的考虑,而不是使用for简单迭代。

这是一个使用命令式和声明式范例计算阶乘的示例:

 func funcFactorial(num int) int { if num == 0 { return 1 } return num * funcFactorial(num-1) } func imperativeFactorial(num int) int { var result int = 1 for ; num > 0; num-- { //    for result *= num } return result } func main() { fmt.Println(funcFactorial(20)) //        fmt.Println(imperativeFactorial(20)) //      } 


现在,递归函数的效率很低。 让我们尝试重写一下它,以优化其计算速度:

 func factTailRec(num int) int { return factorial(1, num) //    ""  } func factorial(accumulator, val int) int { if val == 1 { return accumulator } return factorial(accumulator*val, val-1) } func main() { fmt.Println(factTailRec(20)) // 2432902008176640000 } 


我们的阶乘计算速度略有提高。 我不会提供基准)。

不幸的是,Go并没有开箱即用地实现递归优化,因此您必须自己优化递归尾部。 尽管毫无疑问,可以找到有关此主题的有用的库。 例如,在此主题上有一个“ Loadash for Golang”很酷。

惰性计算


在编程理论中,惰性计算(也称为“延迟计算”)是将计算推迟到需要时进行的过程。 Golang开箱即用不支持延迟计算,因此我们只能模拟以下情况:

 func mult(x, y int) int { fmt.Println(" ") return x * x. } func divide(x, y int) int { fmt.Println(" ") return x / y //    -  } func main() { fmt.Println(multOrDivide(true, mult, divide, 17, 3)) //   ""   ,   1  , //         fmt.Println(multOrDivide(false, mult, divide, 17, 3)) } //  if - else    ""  func multOrDivide(add bool, onMult, onDivide func(t, z int) int, t, z int) int { if add { return onMult(t, z) } return onDivide(t, z) } 


多数情况下,“模拟”惰性表达式不值得,因为它们会使代码过于复杂,但是,如果您的函数很难管理,则应使用此方法。 但是您可以求助于其他解决方案,例如这些解决方案。



仅此而已。 我们仅对Golang的功能范例进行了介绍。 不幸的是,必须模拟部分可能性。 部分,包括完全开发的功能技术(例如monad)未包括在此处,因为在中心上的Go中有很多关于它们文章。 语言本身仍有许多改进之处,例如,随着下一个大版本(GO 2),通用语言有望出现在该语言中。 好吧,我们将拭目以待)。

Source: https://habr.com/ru/post/zh-CN476346/


All Articles