深入理解Scala函数式编程过程
深入理解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)
如有疑问请留言或者到本站社区交流讨论,感谢阅读希望能帮助到大家,谢谢大家对本站的支持!
上一篇:Scala基础简介及代码示例
栏 目:Scala
下一篇:浅谈Scala的Class、Object和Apply()方法
本文标题:深入理解Scala函数式编程过程
本文地址:https://www.xiuzhanwang.com/a1/Scala/12003.html
您可能感兴趣的文章
- 01-11php下关于Cannot use a scalar value as an array的解决办法
- 01-11PHP警告Cannot use a scalar value as an array的解决方法
- 01-11Windows7下安装Scala 2.9.2教程
- 01-11浅谈Scala的Class、Object和Apply()方法
- 01-11Scala基础简介及代码示例
- 01-11Scala安装及环境图文配置教程
- 01-11利用Gradle如何构建scala多模块工程的步骤详解
- 01-11linux下搭建scala环境并写个简单的scala程序
- 01-11Scala的文件读写操作与正则表达式
- 01-11详解如何使用Spark和Scala分析Apache访问日志
阅读排行
本栏相关
- 01-11php下关于Cannot use a scalar value as an ar
- 01-11PHP警告Cannot use a scalar value as an array的
- 01-11Windows7下安装Scala 2.9.2教程
- 01-11浅谈Scala的Class、Object和Apply()方法
- 01-11深入理解Scala函数式编程过程
- 01-11Scala基础简介及代码示例
- 01-11Scala安装及环境图文配置教程
- 01-11linux下搭建scala环境并写个简单的sca
- 01-11利用Gradle如何构建scala多模块工程的步
- 01-11Scala的文件读写操作与正则表达式
随机阅读
- 08-05DEDE织梦data目录下的sessions文件夹有什
- 01-10使用C语言求解扑克牌的顺子及n个骰子
- 01-10C#中split用法实例总结
- 04-02jquery与jsp,用jquery
- 08-05织梦dedecms什么时候用栏目交叉功能?
- 01-11ajax实现页面的局部加载
- 01-11Mac OSX 打开原生自带读写NTFS功能(图文
- 08-05dedecms(织梦)副栏目数量限制代码修改
- 01-10SublimeText编译C开发环境设置
- 01-10delphi制作wav文件的方法