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

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

(0)

相关推荐

  • Scala小程序详解及实例代码

    Scala小程序详解 1. 交互式模式 在命令行窗口中,输入Scala命令: xiaosi@Qunar:~$ scala Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_91). Type in expressions for evaluation. Or try :help. scala> 第一个小程序: xiaosi@Qunar:~$ scala Welcome to Scala 2.11.8 (

  • Windows7下安装Scala 2.9.2教程

    1. 下载Scala 2.9.2 由于最新的Scala 2.10稳定版还没完成,所以最好是下载最新的Scala稳定版:2.9.2版. 下载地址:http://www.scala-lang.org/downloads/distrib/files/scala-2.9.2.msi 下载msi版本的好处在于,环境变量自动配置,否则你需要手动设置两个环境变量. SCALA_HOME环境变量,指向Scala的安装目录. PATH环境变量,要包含 %SCALA_HOME%\bin的值. 2. 安装Scala

  • ExecuteReader(),ExecuteNonQuery(),ExecuteScalar(),ExecuteXmlReader()之间的区别

    ExecuteReader(),ExecuteNonQuery(),ExecuteScalar(),ExecuteXmlReader()之间的区别      主要有这么几种,cmd.ExecuteReader();cmd.ExecuteNonQuery();cmd.ExecuteScalar();cmd.ExecuteXmlReader();) 1,ExecuteReader();返回一个SqlDataReader对象或OleDbDataReader对象,这个看你的程序的需要去 做.可以通过这个

  • PHP警告Cannot use a scalar value as an array的解决方法

    看到php的错误日志里有些这样的提示: [27-Aug-2011 22:26:12] PHP Warning: Cannot use a scalar value as an array in /www/hx/enjoy.php on line 14 [27-Aug-2011 22:26:18] PHP Warning: Cannot use a scalar value as an array in /www/hx/enjoy.php on line 14 检查源程序,大概是下面这样子: 复制

  • php下关于Cannot use a scalar value as an array的解决办法

    今天在测试php程序的时候,出现了一个错误提示:Cannot use a scalar value as an array,这个错误提示前几天也出过,当时好像稍微调了一下就好了,也没深究,今天却又出现了. 不能再糊弄了,得找出原因和解决办法,于是我就去网上搜啊找啊,找了很久都没找到结果,不是网上搜不到这样的问题,而是很少有人做正面的准确的回答.最后这篇文章的一段文字让我一下子弄明白了怎么回事. -------------- 需要注意的是类型的转换: 如果一个变量名(如a)已经被定义为非数组类型,

  • play for scala 实现SessionFilter 过滤未登录用户跳转到登录页面

    一.编写SessionFilter.scala代码 package filters import javax.inject.{Inject, Singleton} import akka.stream.Materializer import controllers.routes import play.api.mvc.{Filter, RequestHeader, Result, Results} import scala.concurrent.Future @Singleton class S

  • 浅谈Scala的Class、Object和Apply()方法

    Scala中如果一个Class和一个Object同名,则称Class是Object的伴生类.Scala没有Java的Static修饰符,Object下的成员和方法都是静态的,类似于Java里面加了Static修饰符的成员和方法.Class和Object都可以定义自己的Apply()方法,类名()调用Object下的Apply()方法,变量名()调用Class下的Apply()方法. class ApplyTest{ def apply() { println("This is a class,

  • Scala解析Json字符串的实例详解

    Scala解析Json字符串的实例详解 1. 添加相应依赖 Json解析工具使用的 json-smart,曾经对比过Java的fastjson.gson.Scala的json4s.lift-json.其中 json-smart 解析速度是最快的. <dependency> <groupId>net.minidev</groupId> <artifactId>json-smart</artifactId> <version>2.3<

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

  • Scala函数式编程专题--scala基础语法介绍

    上次我们介绍了函数式编程的好处,并使用scala写了一个小小的例子帮助大家理解,从这里开始我将真正开始介绍scala编程的一些内容. 这里会先重点介绍scala的一些语法.当然,这里是假设你有一些java或者python的基础,毕竟大部分人不会将scala当作第一门学习编程的语言. 不过这些语法知识记不住也没关系,本身语法这种东西就应该在使用中被记住.这里写这篇的目的也只是梳理一遍,方便大家对语法有个初步的印象,后面可以随时查询. PS:所使用的版本是scala 2.11.8,那我们开始吧 一.

  • Scala函数式编程专题--scala集合和函数

    前情提要: Scala函数式编程专题-- 函数式思想介绍 scala函数式编程专题--scala基础语法介绍 前面已经稍微介绍了scala的常用语法以及面向对象的一些简要知识,这次是补充上一章的,主要会介绍集合和函数. 注意噢,函数和方法是不一样的,方法是在类里面定义的,函数是可以单独存在的(严格来说,在scala内部,每个函数都是一个类) 一.scala集合介绍 还记得上一章介绍的object的apply方法吗,很多数据结构其实都用到了它,从而让我们可以直接用List(...)这样来新建一个L

  • Scala函数式编程专题--函数思想介绍

    为什么我们需要学习函数式编程?或者说函数式编程有什么优势?这个系列中我会用 scala 给你讲述函数式编程中的优势,以及一些函数式的哲学.不懂 scala 也没关系,scala 和 java 是类似的,在每篇的开头我也会先说明这节中用到的 scala 语法. 为什么函数式编程这几年火起来 如 Python 一样,函数式编程(FP,即Functional Programming)也是近几年才逐渐为人们所知,但它并不是一个多么新的概念.它拥有和面向对象编程(OOP)几乎等长的历史.但纵观每件事的脉络

  • 理解javascript函数式编程中的闭包(closure)

    闭包(closure)是函数式编程中的概念,出现于 20 世纪 60 年代,最早实现闭包的语言是 Scheme,它是 LISP 的一种方言.之后闭包特性被其他语言广泛吸纳. 闭包的严格定义是"由函数(环境)及其封闭的自由变量组成的集合体."这个定义对于大家来说有些晦涩难懂,所以让我们先通过例子和不那么严格的解释来说明什么是闭包,然后再举例说明一些闭包的经典用途. 什么是闭包 通俗地讲, JavaScript 中每个的函数都是一个闭包,但通常意义上嵌套的函数更能够体 现出闭包的特性,请看

  • Python函数式编程之面向过程面向对象及函数式简析

    目录 Python 函数式编程 同一案例的不同写法,展示函数式编程 面向过程的写法 面向对象的写法 接下来进入正题,函数式编程的落地实现 Python 函数式编程的特点 纯函数 Python 函数式编程 Python 不是纯粹的函数式语言,但你可以使用 Python 进行函数式编程 典型的听君一席话,如听一席话,说白了就是 Python 具备函数式编程的特性, so,可以借用函数式语言的设计模式和编程技术,把代码写成函数式编程的样子 一般此时我会吹嘘一下,函数式代码比较简洁和优雅~ 好了,已经吹

  • 《JavaScript函数式编程》读后感

    本文章记录本人在学习 函数式 中理解到的一些东西,加深记忆和并且整理记录下来,方便之后的复习. 在近期看到了<JavaScript函数式编程>这本书预售的时候就定了下来.主要目的是个人目前还是不理解什么是函数式编程.在自己学习的过程中一直听到身边的人说面向过程编程和面向对象编程,而函数式就非常少.为了自己不要落后于其他同学的脚步,故想以写笔记的方式去分享和记录自己阅读中所汲取的知识. js 和函数式编程 书中用了一句简单的话来回答了什么是函数式编程: 函数式编程通过使用函数来将值转换为抽象单元

  • JavaScript 函数式编程实践(来自IBM)第1/3页

    函数式编程简介 说到函数式编程,人们的第一印象往往是其学院派,晦涩难懂,大概只有那些蓬头散发,不修边幅,甚至有些神经质的大学教授们才会用的编程方式.这可能在历史上的某个阶段的确如此,但是近来函数式编程已经在实际应用中发挥着巨大作用了,而更有越来越多的语言不断的加入诸如 闭包,匿名函数等的支持,从某种程度上来讲,函数式编程正在逐步"同化"命令式编程. 函数式编程思想的源头可以追溯到 20 世纪 30 年代,数学家阿隆左 . 丘奇在进行一项关于问题的可计算性的研究,也就是后来的 lambd

  • 探究JavaScript函数式编程的乐趣

    编程范式 编程范式是一个由思考问题以及实现问题愿景的工具组成的框架.很多现代语言都是聚范式(或者说多重范式): 他们支持很多不同的编程范式,比如面向对象,元程序设计,泛函,面向过程,等等. 函数式编程范式 函数式编程就像一辆氢燃料驱动的汽车--先进的未来派,但是还没有被广泛推广.与命令式编程相反,他由一系列语句组成,这些语句用于更新执行时的全局状态.函数式编程将计算转化作表达式求值.这些表达式全由纯数学函数组成,这些数学函数都是一流的(可以被当做一般值来运用和处理),并且没有副作用. 函数式编程

  • Python装饰器的函数式编程详解

    Python的装饰器的英文名叫Decorator,当你看到这个英文名的时候,你可能会把其跟Design Pattern里的Decorator搞混了,其实这是完全不同的两个东西.虽然好像,他们要干的事都很相似--都是想要对一个已有的模块做一些"修饰工作",所谓修饰工作就是想给现有的模块加上一些小装饰(一些小功能,这些小功能可能好多模块都会用到),但又不让这个小装饰(小功能)侵入到原有的模块中的代码里去.但是OO的Decorator简直就是一场恶梦,不信你就去看看wikipedia上的词条

随机推荐