scala成长之路(6)函数入门

众所周知,scala作为一门极客型的函数式编程语言,支持的特性包括:

  • 函数拥有“一等公民”身份;
  • 支持匿名函数(函数字面量)
  • 支持高阶函数
  • 支持闭包
  • 部分应用函数
  • 柯里化

首先需要指出,在scala中有方法函数对象两种形态,方法即是通过def关键字定义的函数,而函数对象则是通过将方法转换而来,或lambda赋值而来。

1. 从“一等公民”说起

很多稍微了解过函数式编程的人可能都听说过“一等公民”这种说法,但却很少有人能明明白白地说出究竟什么是“一等公民”。这里我做个类比你马上就明白了:现实中,什么样的人能被当做一等公民?首先,他必须是个独立的个体——依赖父母或朋友才能生存的人肯定不能被当做公民,更不用说一等了;其次,这个人必须拥有足够的自由——既能上九天揽月,又可下五洋捉鳖,方才能是一等公民。对应到我们的函数式编程,我们可以总结出几个点:

(1) 函数的定义和调用不依赖其他结构,例如C、python、js、scala,而反面典型就是java,因为java的任何函数(方法)都必须定义在类、接口、枚举(其实也是类)中,而且任何的方法调用都要通过对象、类的静态方法或接口(jdk 1.8),方法不可能直接调用,必须依附于其他结构而存在。所以这种情况下函数肯定不是“一等公民”。

(2)函数可以作为函数的参数、返回值,并可以对函数进行变量赋值,而且函数的定义位置极度自由,任何代码块里又能定义函数。

现在我们再来看scala,它完美地契合上边所有的需求(但是注意,除了脚本形式的scala之外,其他的scala程序也只能包含在class或object中),scala中函数支持在函数内部定义,而且使用lambda表达式定义的函数可以赋值给任何变量、常量,所有函数均可作为返回值、参数。

2. lambda表达式的学问

很多scala初学者都倒在了scala的lambda上,因为scala lambda的灵活多样,导致很多时候你可能都看不懂。下面我们从最基本的讲起:

最基本的:

val fun = (a:Int) => {a < 100 && a > 0}

当r定义的参数为函数时:

def fun1(f:String => Unit) = f("wangyalou")

我们可以方便地使用lambda传入需要的函数:

fun1((s:String)=>{println(s)})

注意了,一般人都不这么写,因为作为参数的lambda可~以~简~写~~~~~准备好我要开始啰!首先,省略掉可以推断出来的类型参数:

fun1((s)=>{println(s)})

当只有一个参数时,=>前的()可省:

fun1(s=>{println(s)})

还可再简化,scala中可以用_代替只出现一次的参数:

fun1(println(_))  或 fun1(println _)

最后,我们甚至连下划线都可以不要了:

fun1(println)

注意,最后的情况我们是利用了编译器支持lambda的“eta转换”,即在表达式只有一个参数,且整个执行部分就是一个函数调用时,可以直接写函数名

插一句:eta扩展(eta-expression)是另一个东西,指的是将一个普通方法转换为函数对象的过程:

val b= too(_,_,_)
val b= too _    //这也可以?是的
val b : (Int,Int,Int) =>Int = foo

其中too为一个参数为3个Int的方法

但是,too(_,_,1)一定不是eta扩展!

下划线的用法博大精深,这里再给出一些例子:

例1:lambda作为参数,_可代替只用一次的参数,且省掉“=>”val nums = Array(1,2,3,4)
nums.filter(_>2)
运行结果:
res31: Array[Int] = Array(3, 4)
例2:lambda作为参数,_可代替只用一次的参数,且省掉“=>”

scala> def foo(f:(Int,Int)=>Int)(a:Int,b:Int) = f(a,b)
foo: (f: (Int, Int) => Int)(a: Int, b: Int)Int

scala> foo(_+_)(3,4)
res33: Int = 7

例3:lambda作为函数定义,_可代替只用一次的参数,且省掉“=>”,但这时要加上类型,因为这里无法推断出“_”的类型

val b = (_:Int) + (_:Int)

b(1,2)

运行结果:

res32: Int = 3

3. 部分应用函数(偏函数)

一个例子足以说清楚:

scala> def foo(a:Int, b:Int, c:Boolean) = if(c) a+b else a-b
foo: (a: Int, b: Int, c: Boolean)Int
scala> val foom = foo(_:Int,_:Int,false)
foom: (Int, Int) => Int = <function2>

类似于python中的偏函数,这里将某个参数确定,其他参数用"_"代替并指明其类型,注意一定要指明类型啊!!!不然就成了eta扩展失败的案例了!!

原文地址:https://www.cnblogs.com/wangyalou/p/9588941.html

时间: 2024-11-02 11:08:55

scala成长之路(6)函数入门的相关文章

scala成长之路:问题记录

scala成长之路(5)问题记录 还是在看scala sdk源码的时候,有很多问题要考自己慢慢摸索,这里做个记录. 一. 隐式转换的作用域? 隐式转换需要三个因素 1. 己方(当前对象) 2. 转换函数 3. 对方(转换的目标类) 这三个需要在同一个作用域内才能生效吗?举个简单的例子,依然是java HashSet隐式转换为scala Set(可以参看本系列(3)),我们只是在要用到转换的文件里写了一行: import scala.collection.JavaConverters._ 也就是说

scala成长之路(7)函数进阶——可能是史上最浅显易懂的闭包教程

由于scala中函数内部能定义函数,且函数能作为函数的返回值,那么问题来了,当返回的函数使用了外层函数的局部变量时,会发生什么呢?没错,就产生是闭包. 关于闭包的解释网上一大堆,但基本上都是照葫芦画瓢,一个模子刻出来的,说来说去都只讲了"内部函数引用外层函数的局部变量"这个刻板的定义,根本没降到精髓.精髓是什么?且看我一句话给你讲清楚: 闭包就是外层函数函数的对象,外层函数就是闭包的构造函数! 怎么样,是不是摸不着头脑?其实是这样,我们使用闭包的目的并不是为了引用外层函数的局部变量,这

scala成长之路(2)对象和类

scala提供了一种特殊的定义单例的方法:object关键字 scala> object Shabi{ | val age = 0 | val name = "shabi" | def say = { | println("hello, I am a shabi") | } | }defined object Shabi scala> Shabi.sayhello, I am a shabi object定义的单例可以类比于java中的静态元素,也类似于

scala成长之路(4)compaion object——伴生对象的使用

虽然java一直声称自己是完全面向对象的语言,但一直以来都被很多人所质疑,其中java的静态成员函数就是主要的"罪魁祸首".由于java中保留了静态方法的调用,导致其编程模式依然有过程式编程的可能,尤其是在静态方法被滥用的当下(一个顽固的面向过程程序员完全可以将他的过程思维用一大推静态方法实现,而class仅仅只是个空有其表的外壳而已) 所以在scala中,设计者摒弃了static关键字,宣称自己是完全面向对象的,似乎更有说服力... 但是我们在编程中有时确实需要用到一般功能性函数,例

Scala 编程详解 ---003 函数入门

一.函数的定义与调用 在Scala中定义函数时,需要定义函数的函数名.参数.函数体. 我们的第一个函数如下所示: def sayHello(name: String, age: Int) = { if (age > 18) { printf("hi %s, you are a big boy\n", name); age } else { printf("hi %s, you are a little boy\n", name); age } sayHello

物联网架构成长之路(13)-SpringBoot入门

1. 前言 下载最新版的JavaEE eclipse-jee-oxygen-2-win32-x86_64.zip 安装STS插件 Window->Eclipse Marketplace -> popular 下那个 Spring Tools(aka Spring IDE and Spring Tool Suite) 然后通过STS工具创建一个新的Spring boot工程,这里就不细说了.网上资料很多,也比较简单就可以搭建起来.后面对SpringBoot也只是简单的提一下,还有说一下注意点.没

python成长之路第三篇(1)_初识函数

目录: 函数 1.为什么要使用函数 2.什么是函数 3.函数的返回值 4.文档化函数 5.函数传参数 文件操作(二) 1.文件操作的步骤 2.文件的内置方法 函数: 一.为什么要使用函数 在日常写代码中,我们会发现有很多代码是重复利用的,这样会使我们的代码变得异常臃肿,比如说: 我们要写一个验证码的功能 例子: 比如说我们要进行一些操作,而这些操作需要填写验证码 验证码代码:  1 import random   2 number_check = ''   3 for i in range(0,

Scala学习之路----基础入门

一.Scala解释器的使用 REPL:Read(取值)-> Evaluation(求值)-> Print(打印)-> Loop(循环) scala解释器也被称为REPL,会快速编译scala代码为字节码,然后交给JVM来执行. 计算表达式:在scala>命令行内,键入scala代码,解释器会直接返回结果. 如果你没有指定变量来存放这个值,那么值默认的名称为res,而且会 显示结果的数据类型,比如Int.Double.String等等. 例如,输入1 + 1,会看到res0: Int

java实习生的成长之路&lt;转&gt;

java实习生的成长之路<转> 首先初识语法的阶段,必须要学会怎么操作对象,操作if和for,操作list set map,然后是线程.IO和jdbc什么的,其余的,若是一时不理解,可以后边需要时再学. 这阶段完了,你可以写些能在控制台打印出来的小程序,锻炼下逻辑思维.也就是号称JAVASE毕业了,其实不过是入门而已,如果要往WEB方向发展,这些倒是基本足够了. 接下来要学html,jsp,servlet,javascript,oracle,tomcat,目标,写出第一个动态网站,也 许只是个