深入理解Scala函数式编程过程

所属分类: 软件编程 / 其它相关 阅读数: 535
收藏 0 赞 0 分享

深入理解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)
 

如有疑问请留言或者到本站社区交流讨论,感谢阅读希望能帮助到大家,谢谢大家对本站的支持!

更多精彩内容其他人还在看

Idea 无法引用类问题解决办法

这篇文章主要介绍了 Idea 无法引用类问题解决办法的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

Unity3D中shader 轮廓描边效果实现代码

这篇文章主要介绍了Unity3D中shader 轮廓描边效果的相关资料,需要的朋友可以参考下
收藏 0 赞 0 分享

8个基于Lucene的开源搜索引擎(推荐)

Lucene是一种功能强大且被广泛使用的搜索引擎,以下列出8种基于Lucene的搜索引擎,你可以想象他们有多么强大
收藏 0 赞 0 分享

计算机二级如何一次性通过?给NCRE焦躁心情降温!

计算机二级到现阶段应该如何备考,该听什么课?该针对哪些考点重点学习,这些都要做到心里有数,有计划性。这篇文章为大家分享了计算机二级备考技巧,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
收藏 0 赞 0 分享

HBuilder打包App方法(图文教程)

下面小编就为大家带来一篇HBuilder打包App方法(图文教程)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
收藏 0 赞 0 分享

深入理解Scala函数式编程过程

这篇文章主要介绍了深入理解Scala函数式编程过程的相关资料,希望通过本文能帮助到大家,让大家学习理解这部分内容,需要的朋友可以参考下
收藏 0 赞 0 分享

权限控制之粗粒度与细粒度概念及实现简单介绍

这篇文章主要介绍了权限控制之粗粒度与细粒度概念及实现简单介绍,具有一定参考价值,需要的朋友可以了解下。
收藏 0 赞 0 分享

Hbuilder配置Avalon和Vue指令提示的方法详解

HBuilder是DCloud(数字天堂)推出的一款支持HTML5的Web开发IDE,下面这篇文章主要给大家介绍了关于Hbuilder如何配置Avalon和Vue指令提示的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或工作具有一定的参考学习价值,需要的朋友们下面来一起看
收藏 0 赞 0 分享

Hbuilder连远程接服务器上传代码的图文教程

下面小编就为大家分享一篇Hbuilder连远程接服务器上传代码的图文教程,具有很好的参考价值,一起跟随小编过来看看吧,希望对大家有所帮助
收藏 0 赞 0 分享

App开发建议技巧

有同学问我,对应用开发你有没有值得注意或小技巧的地方可以分享的。比如适配、优化、排查错误什么的。鸡排把自己的总结笔记整理出来了。供大家参考
收藏 0 赞 0 分享
查看更多