【Scala】高阶函数和柯里化

高阶函数

在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:

- 接受一个或多个函数作为输入
- 输出一个函数

在数学中它们也叫做算子(运算符)或泛函。微积分中的导数就是常见的例子,因为它映射一个函数到另一个函数。

高阶函数的例子

假设有一个函数对给定两个数区间中的所有整数求和:

def sumInts(a: Int, b: Int): Int =
  if(a > b) 0 else a + sumInts(a + 1, b)

如果现在要求连续整数的平方和:

def square(x: Int): Int = x * x
def sumSquares(a: Int, b: Int): Int =
  if(a > b) 0 else square(a) + sumSquares(a + 1, b)

如果要计算2的幂次的和:

def powerOfTwo(x: Int): Int = if(x == 0) 1 else 2 * powerOfTwo(x-1)
def sumPowersOfTwo(a: Int, b: Int): Int =
  if(a > b) 0 else powerOfTwo(a) + sumPowersOfTwo(a+1, b)

上面的函数都是从a到b的f(n)的累加形式,我们可以抽取这些函数中共同的部分重新编写函数sum,其中定义的f作为一个参数传入到高阶函数sum中:

def sum(f: Int => Int, a: Int, b: Int): Int =
  if(a > b) 0 else f(a) + sum(f, a+1, b)

def id(x: Int): Int = x
def square(x: Int): Int = x * x
def powerOfTwo(x: Int): Int = if(x == 0) 1 else 2 * powerOfTwo(x-1)

def sumInts(a: Int, b: Int): Int = sum(id, a, b)
def sumSquared(a: Int, b: Int): Int = sum(square, a, b)
def sumPowersOfTwo(a: Int, b: Int): Int = sum(powerOfTwo, a, b)

有用的高阶函数

map方法将一个函数应用到某个集合的所有元素并返回结果;foreach将函数应用到每个元素。

//打印三角形
scala> (1 to 9).map("^" * _).foreach(println _)
^
^^
^^^
^^^^
^^^^^
^^^^^^
^^^^^^^
^^^^^^^^
^^^^^^^^^

filter方法输出所有匹配某个特定条件的元素:

scala> (1 to 9).filter(_ % 2 == 0)
res14: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8)

可以通过练习使用Scala集合库中的一些常用的接受函数参数的方法来熟悉高阶函数。

柯里化

柯里化(Currying)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数。

返回函数的函数

在上面高阶函数的例子中,我们通过def sumInts(a: Int, b: Int): Int = sum(x => x, a, b)def sumSquared(a: Int, b: Int): Int = sum(x => x*x, a, b)来定义新的函数,上面这两个函数每次都要传入a和b两个参数到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
}

//于是得到如下定义,这样就简化了参数
def sumInts = sum(x => x)
def sumSquared = sum(x => x * x)
def sumPowersOfTwo = sum(powerOfTwo)

多个参数列表

根据上面的例子,我们能不能更加简化,省去sumIntssumSquaredsumPowersOfTwo这几个中间函数的形式呢?

通过sum(square)(1, 10)函数来替代sumSquared函数。

一般情况,这类函数的左结合的:sum(square)(1, 10) == (sum(square))(1, 10)

我们可以这样定义sum函数:

def sum(f: Int => Int)(a: Int, b: Int): Int =
  if(a > b) 0 else f(a) + sum(f)(a + 1, b)

这使得函数编写更加简洁。

一般的,多参数函数定义为def f(args1)...(argsn) = E,

当n > 1时,等同于def f(args1)...(args n-1) = {def g(argsn) = E; g}或者def f(args1)...(args n-1) = (argsn => E)

如果重复这个过程n次,得到def f = (args1 => (args2 => ... (argsn => E) ) )

这种函数定义称为柯里化(Currying)。

转载请注明作者Jason Ding及其出处

GitCafe博客主页(http://jasonding1354.gitcafe.io/)

Github博客主页(http://jasonding1354.github.io/)

CSDN博客(http://blog.csdn.net/jasonding1354)

简书主页(http://www.jianshu.com/users/2bd9b48f6ea8/latest_articles)

百度搜索jasonding1354进入我的博客主页

时间: 2024-10-01 03:31:18

【Scala】高阶函数和柯里化的相关文章

理解运用JS的闭包、高阶函数、柯里化

一.闭包 1. 闭包的概念 闭包与执行上下文.环境.作用域息息相关 执行上下文 执行上下文是用于跟踪运行时代码求值的一个规范设备,从逻辑上讲,执行上下文是用执行上下文栈(栈.调用栈)来维护的. 代码有几种类型:全局代码.函数代码.eval代码和模块代码:每种代码都是在其执行上下文中求值. 当函数被调用时,就创建了一个新的执行上下文,并被压到栈中 - 此时,它变成一个活动的执行上下文.当函数返回时,此上下文被从栈中弹出 function recursive(flag) { // Exit cond

js高阶函数--判断数据类型、函数胡柯里化;

一.判断数据类型: 常见的判断有typeof.instanceof. constructor. prototype,先来看typeof: var a = "hello world"; var b = 10; var c = [1, 3, 5]; var d = new Date(); var e = function() {}; console.log(typeof a); // string console.log(typeof b); // number console.log(t

浅析 JavaScript 中的 函数 currying 柯里化

原文:浅析 JavaScript 中的 函数 currying 柯里化 何为Curry化/柯里化? curry化来源与数学家 Haskell Curry的名字 (编程语言 Haskell也是以他的名字命名). 柯里化通常也称部分求值,其含义是给函数分步传递参数,每次传递参数后部分应用参数,并返回一个更具体的函数接受剩下的参数,这中间可嵌套多层这样的接受部分参数函数,直至返回最后结果.因此柯里化的过程是逐步传参,逐步缩小函数的适用范围,逐步求解的过程. 柯里化一个求和函数 按照分步求值,我们看一个

scala入门-09 scala高阶函数

我们做spark开发 会使用很多spark的高阶函数 所以 今天我就在linux服务上使用scala高阶函数 声明一个List集合: List集合所在的包已经被预定义自动引入,所以此处就不需要在引入包了,这里直接使用List实例化对象,其实用List的object对象的apply方法 我们使用map函数把list中的每个值都乘以3: x表示l中每一个元素,map对l中的每一个元素进行遍历操作,由于List中只有一种类型的元素,所以我们在执行马屁操作的时候可以省略其类型,如下所示: List集合中

深入详解函数的柯里化

深入详解函数的柯里化 一.补充知识点之函数的隐式转换 JavaScript作为一种弱类型语言,它的隐式转换是非常灵活有趣的.当我们没有深入了解隐式转换的时候可能会对一些运算的结果会感动困惑,比如4 + true = 5.当然,如果对隐式转换了解足够深刻,肯定是能够很大程度上提高对js的使用能力.只是我没有打算将所有的隐式转换规则分享给大家,这里暂时只分享一下,函数在隐式转换中的一些规则. 来一个简单的思考题. 1 2 3 4 5 function fn() { return 20; } cons

Effective JavaScript Item 26 使用bind来进行函数的柯里化(Curry)

本系列作为Effective JavaScript的读书笔记. 在上一个Item中介绍了bind的一种用法:用来绑定this对象.但是实际上,bind含有另一种用法,就是帮助函数进行柯里化.关于柯里化,这里有一份百科可以参考: http://zh.wikipedia.org/wiki/%E6%9F%AF%E9%87%8C%E5%8C%96 但是实际上,关于柯里化只需要记住一点就够了:柯里化是把接受多个参数的函数变换成接受一个单一参数(通常是最初函数的第一个参数,但是并无限制)的函数,并且返回这个

关于arguments对象以及函数的柯里化;

1.arguments对象 Arguments是个类似数组但不是数组的对象,说他类似数组是因为其具备数组相同的访问性质及方式,能够由arguments[n]来访问对应的单个参数的值,并拥有数组长度属性length.还有就是arguments对象存储的是实际 传递给函数的参数,而不局限于函数声明所定义的参数列表,而且不能显式创建 arguments 对象.在JavaScript中函数不介意传递进来多少参数,也不会因为参数不统一而错误.实际上,函数体内可以通过 arguments 对象来接收传递进来

Scala 系列(十)—— 函数 & 闭包 & 柯里化

一.函数 1.1 函数与方法 Scala 中函数与方法的区别非常小,如果函数作为某个对象的成员,这样的函数被称为方法,否则就是一个正常的函数. // 定义方法 def multi1(x:Int) = {x * x} // 定义函数 val multi2 = (x: Int) => {x * x} println(multi1(3)) //输出 9 println(multi2(3)) //输出 9 也可以使用 def 定义函数: def multi3 = (x: Int) => {x * x}

Scala 高阶函数(high-order function)剖析

Scala 是一种函数式编程语言,也就是说每一个函数都是一个值.Scala 有很简洁的语法用于定义匿名函数.curry 化函数(curried function).应用函数.偏函数以及嵌套函数等.函数式编程由数学函数演变得来,包含大量诸如串联与并联.组合与分离.协变与逆变等一系列概念.本文将就如何实现高阶函数进行阐述,其他部分内容不进行深究. 类型参数化协变与逆协变 类型参数化(type parameterization)由类型协变性(Type variance)补充构成,类型协变性的机制则是在