时间:2021-05-19
深入理解Scala函数式编程过程
我们马上开始一段变态的过程
如果要求立方和,可以这么做
35 * 35 * 35 68 * 68 * 68没毛病,抽象一点儿,写个函数:
def cube(n: Int) = n * n * n cube(35) cube(68)省事儿了,如果求1到10的立方和,OK,写个递归
def cube(n: Int) = n * n * n def sumCube(a: Int, b: Int): Int = if (a > b) 0 else cube(a) + sumCube(a + 1, b) sumCube(1, 10)变态一点儿,立方和,平方和,阶乘和,依旧写出它们的函数并且依次计算没毛病
def cube(n: Int) = n * n * n def id(n: Int) = n def square(n : Int) = n * n def fact(n: Int): Int = if (n == 0) 1 else n * fact(n - 1) def sumCube(a: Int, b: Int): Int = if (a > b) 0 else cube(a) + sumCube(a + 1, b) def sumSquare(a: Int, b: Int): Int = if(a > b) 0 else square(a) + sumSquare(a + 1, b) def sumFact(a: Int, b: Int): Int = if (a > b) 0 else fact(a) + sumFact(a + 1, b) def sumInt(a: Int, b: Int): Int = if(a > b) 0 else id(a) + sumInt(a + 1, b) sumCube(1, 10) sumInt(1, 10) sumSquare(1, 10) sumFact(1, 10)然后你发现,你已经写了一堆同样逻辑的if else,看起来不奇怪么,这种无脑的操作当然要避免:
我们要把这些函数名不同但是处理逻辑相同的渣渣都封装到一个函数中,并且这个函数将作为参数赋值到高阶函数中,运行的结果只跟传入的参数类型有关系,也就是把cube,square,fact,泛化成一个f
def cube(n: Int) = n * n * n def id(n: Int) = n def square(n : Int) = n * n def fact(n: Int): Int = if (n == 0) 1 else n * fact(n - 1) //高阶函数def sum(f: Int=>Int, a:Int, b:Int): Int = if(a>b) 0 else f(a)+sum(f, a+1, b)// 使用高阶函数重新定义求和函数def sumCube(a: Int, b: Int): Int = sum(cube, a, b) def sumSquare(a: Int, b: Int): Int = sum(square, a, b) def sumFact(a: Int, b: Int): Int = sum(fact, a, b) def sumInt(a: Int, b: Int): Int = sum(id, a, b) sumCube(1, 10) sumInt(1, 10) sumSquare(1, 10) sumFact(1, 10)但是这样写,还有个问题,就是前面定义了一堆cube,id的初始定义,后面还要继续定义,实际上就是套了一层包装,不要了,去掉,使用匿名函数的功能来将调用进一步简化。多数情况下,我们关心的是高阶函数,而不是作为参数传入的函数,所以为其单独定义一个函数是没有必要的。值得称赞的是 Scala 中定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体,参数的类型是可省略的,Scala 的类型推测系统会推测出参数的类型。使用匿名函数后,我们的代码变得更简洁了:
//保留逻辑较为复杂的函数def fact(n: Int): Int = if (n == 0) 1 else n * fact(n - 1) def sum(f: Int => Int, a: Int, b: Int): Int = if (a > b) 0 else f(a) + sum(f, a + 1, b) // 使用高阶函数重新定义求和函数def sumCube(a: Int, b: Int): Int = sum(x => x * x * x, a, b) def sumSquare(a: Int, b: Int): Int = sum(x => x * x, a, b) def sumFact(a: Int, b: Int): Int = sum(fact, a, b) def sumInt(a: Int, b: Int): Int = sum(x => x, a, b) sumCube(1, 10) sumInt(1, 10) sumSquare(1, 10) sumFact(1, 10)写到这里问题解决的差不多了,但是我们仔细想想,函数式编程的真谛,一个输入到另一个输出,而不是像这样两个参数传来传去,看起来很麻烦,于是乎
def fact(n: Int): Int = if (n == 0) 1 else n * fact(n - 1) // 高阶函数def sum(f: Int => Int): (Int, Int) => Int = { def sumF(a: Int, b: Int): Int = if (a > b) 0 else f(a) + sumF(a + 1, b) sumF } // 使用高阶函数重新定义求和函数def sumCube: Int = sum(x => x * x * x) def sumSquare: Int = sum(x => x * x) def sumFact: Int = sum(fact) def sumInt: Int = sum(x => x) // 这些函数使用起来还和原来一样 ! sumCube(1, 10) sumInt(1, 10) sumSquare(1, 10) sumFact(1, 10)实际上这个时候sum里面传入的已经是匿名函数了,类似于g(f(x))里面的f(x), 你还需要去调用那个f(x)而不是去脑补运算.
我们再来开一下脑洞,既然sum返回的是一个函数,我们可以直接使用这些函数,没有必要再重复写一遍调用命令了,sumCube(1, 10) 类的语句可以省去不要了。
def fact(n: Int): Int = if (n == 0) 1 else n * fact(n - 1) // 高阶函数def sum(f: Int => Int): (Int, Int) => Int = { def sumF(a: Int, b: Int): Int = if (a > b) 0 else f(a) + sumF(a + 1, b) sumF }// 直接调用高阶函数 ! sum(x => x * x * x) (1, 10) //=> sumCube(1, 10) sum(x => x) (1, 10) //=> sumInt(1, 10) sum(x => x * x) (1, 10) //=> sumSquare(1, 10) sum(fact) (1, 10) //=> sumFact(1, 10)最后我们还可以使用高阶函数的语法糖来进一步优化这段代码:
// 没使用语法糖的 sum 函数 def sum(f: Int => Int): (Int, Int): Int = { def sumF(a: Int, b: Int): Int = if (a > b) 0 else f(a) + sumF(a + 1, b) sumF } // 使用语法糖后的 sum 函数 def sum(f: Int => Int)(a: Int, b: Int): Int = if (a > b) 0 else f(a) + sum(f)(a + 1, b)我反而觉得用语法糖更容易理解一点,更倾向于我们学的数学语言。
读者可能会问:我们把原来的sum函数转化成这样的形式,好处在哪里?答案是我们获得了更多的可能性,比如刚开始求和的上下限还没确定,我们可以在程序中把一个函数传给sum, sum(fact)完全是一个合法的表达式,待后续上下限确定下来时,再把另外两个参数传进来。对于 sum 函数,我们还可以更进一步,把 a,b 参数再转化一下,这样 sum 函数就变成了这样一个函数:它每次只能接收一个参数,然后返回另一个接收一个参数的函数,调用后,又返回一个只接收一个参数的函数。这就是传说中的柯里化,多么完美的形式!在现实世界中,的确有这样一门函数式编程语言,那就是 Haskell,在 Haskell 中,所有的函数都是柯里化的,即所有的函数只接收一个参数!
// 柯里化后的 sum 函数 def sum(f: Int => Int)(a: Int) (b: Int): Int = if (a > b) 0 else f(a) + sum(f)(a + 1)(b) // 使用柯里化后的高阶函数 ! sum(x => x * x * x)(1)(10) //=> sumCube(1, 10) sum(x => x)(1)(10) //=> sumInt(1, 10)如有疑问请留言或者到本站社区交流讨论,感谢阅读希望能帮助到大家,谢谢大家对本站的支持!
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
前情提要:Scala函数式编程专题——函数式思想介绍scala函数式编程专题——scala基础语法介绍前面已经稍微介绍了scala的常用语法以及面向对象的一些简
为什么我们需要学习函数式编程?或者说函数式编程有什么优势?这个系列中我会用scala给你讲述函数式编程中的优势,以及一些函数式的哲学。不懂scala也没关系,s
上次我们介绍了函数式编程的好处,并使用scala写了一个小小的例子帮助大家理解,从这里开始我将真正开始介绍scala编程的一些内容。这里会先重点介绍scala的
Scala是一门多范式(multi-paradigm)的编程语言,设计初衷是要集成面向对象编程和函数式编程的各种特性。Scala运行在Java虚拟机上,并兼容现
扩展阅读c#基础系列1---深入理解值类型和引用类型c#基础系列2---深入理解String引言在上篇文章深入理解值类型和引用类型的时候,有的小伙伴就推荐说一说