三.函数

       函数是可重用逻辑的核心构件。函数式编程语言特别强调支持创建高可重用、可组合的函数。
       在Scala中,函数式可重用的命名表达式。函数可以参数化,可以返回一个值。
       尽可能构建纯(pure)函数,纯函数式指:

  • 有一个或多个输入参数
  • 只使用输入参数完成计算
  • 返回一个值
  • 对于相同的输入总返回相同的值
  • 不使用或影响函数之外的任何数据
  • 不受函数之外的任何数据的影响
语法:定义无输入的函数
def <identifier> = <expression>

语法:定义函数时指定返回类型
def <identifier>: <type> = <expression>

语法:完整函数定义
def <identifier>(<identifier>: <type>[, ...]): <type> = <expression>
例子:基本数学运算
def multiplier(x: Int, y: Int): Int = { x * y }

       最后一行将成为表达式的返回值,相应也是函数的返回值。有些情况下,可能需要在函数的表达式块结束前退出并返回一个值,可以使用return关键字来显式指定函数返回值。

例子:提前退出
scala> def safeTrim(s: String): String = {
     |   if (s == null) return null
     |   s.trim()
     | }

1.过程

       过程(procedure)是没有返回值的函数。如果有一个简单的函数,没有显式的返回类型,而且最后是一个语句,Scala编译器就会推导出这个函数的返回类型为Unit。

scala> def log(d: Double) = println(f"Got value $d%.2f")
log: (d: Double)Unit

scala> def log(d: Double): Unit = println(f"Got value $d%.2f")
log: (d: Double)Unit

2.用空括号定义函数

       要定义和调用一个无输入的函数(即没有输入参数的函数),可以使用空括号。这样可以很清楚地区分函数和值。

语法:用空括号定义函数
def <identifier>()[: <type>] = <expression>
例子:
def hi(): String = "hi"

注意:有副作用(会修改其范围之外的数据)的函数,在定义时应当加空括号。

3.使用表达式块调用函数

       使用一个参数调用函数时,可以利用一个用大括号包围的表达式块发送参数,而不是用小括号包围值。通过使用表达式块调用函数,可以完成一些计算和其他动作,然后利用这个块的返回值调用函数。

语法:用表达式块调用函数
<function identifier> <expression block>

       有些情况下可能更适合使用表达式块来调用函数,例如必须向函数发送一个计算值时。不必先计算一个量然后把它保存在局部值中再传递给函数,完全可以在表达式块中完成计算。表达式块会在调用函数之前计算,而且表达式块的返回值将用作这个函数的参数。

例子:如何在一个表达式块中计算值来调用一个函数:
scala> def formatEuro(amt: Double) = f"$amt%.2f"
formatEuro: (amt: Double)String

scala> formatEuro(3.4645)
res9: String = 3.46

scala> formatEuro { val rate = 1.32; 0.235 + 0.7123 + rate * 5.32}
res10: String = 7.97

4.递归函数

       递归函数就是调用自身的函数,要检查某类参数或外部条件来避免函数陷入无限循环。

例子:得到一个整数的给定整数次幂
scala> def power(x: Int, n: Int): Long = {
     |   if (n >= 1) x * power(x, n-1)
     |   else 1
     | }
power: (x: Int, n: Int)Long

scala> power(2,8)
res11: Long = 256

5.嵌套函数

       可以在函数中定义另一个内部函数,这个内部函数只能在该函数中使用。

例子:取3个整数,返回其中值最大的整数
scala> def max(a: Int,b: Int,c: Int) = {
     |   def max(x: Int, y: Int) = if (x > y) x else y
     |   max(a,max(b,c))
     | }
max: (a: Int, b: Int, c: Int)Int

scala> max(42,182,19)
res12: Int = 182

       Scala函数按函数名以及其参数类型列表来区分。所以它们之间不会发生冲突。不过,即使函数名和参数类型相同,它们也不会冲突,因为局部(嵌套)函数优先于外部函数。

6.用命名参数调用函数

       调用函数的惯例是按原先定义时的顺序指定参数。但是在Scala中,可以按名调用参数,这样就可以不按顺序指定参数。

语法:按名指定参数
<function name>(<parameter> = <value>)
例子:
scala> def greet(prefix: String,name: String) = s"$prefix $name"
greet: (prefix: String, name: String)String

scala> val greeting = greet(name = "Jack", prefix = "Mr")
greeting: String = Mr Jack

7.有默认值的参数

       可以为任意参数指定默认值,使得调用者可以忽略这个参数。

语法:为函数参数指定默认值
def <identifier>(<identifier>: <type> = <value>): <type>
例子:
scala> def greet(prefix: String = "", name: String) = s"$prefix $name"
greet: (prefix: String, name: String)String

scala> val greeting = greet(name = "Paul")
greeting: String = " Paul"

       可以重新组织这个函数,让必要的参数在前,这样就可以直接调用这个函数而不再需要使用参数名:

scala> def greet(name: String, prefix: String = "") = s"$prefix $name"
greet: (name: String, prefix: String)String

scala> val greeting = greet("Ola")
greeting: String = " Ola"

       最好让必要参数在前,有默认值的参数在后。

8.Vararg参数

       Scala支持vararg参数,所以可以定义输入参数个数可变的函数。vararg参数后面不能跟非vararg,因为无法加以区分。
       要标志一个参数匹配一个或多个输入实参,在函数定义中需要该参数类型后面增加一个星号(*)

例子:vararg参数创建求和函数
scala> def sum(items: Int*): Int = {
     |   var total = 0
     |   for (i <- items) total += i
     |   total
     | }
sum: (items: Int*)Int

scala> sum(10, 20, 30)
res13: Int = 60

9.参数组

       Scala中可以把参数表分解为参数组(parameter groups),每个参数组分别用小括号分隔。

例子:
scala> def max(x:Int)(y:Int) = if (x > y) x else y
max: (x: Int)(y: Int)Int

scala> val larger = max(20)(39)
larger: Int = 39

10.类型参数

       Scala中,可以传递类型参数,类型参数指示了值参数或返回值使用的类型。

语法:定义函数的类型参数
def  <function-name>[type-name](<parameter-name>: <type-name>): <type-name>...
例子:
scala> def identity[A](a: A): A = a
identity: [A](a: A)A

scala> val s: String = identity[String]("Hello")
s: String = Hello

scala> val d: Double = identity[Double](2.717)
d: Double = 2.717

11.方法和操作符

       实际中,函数常存在于对象中,用来处理对象的数据,所以对函数更适合的说法通常是“方法”。
       方法(method)是类中定义的一个函数,这个类的所有实例都会有这个方法。

语法:用中缀点记法调用方法
<class instance>.<method>[(<parameters>)]
例子:加法
scala> d.+(2.721)
res14: Double = 68.363

       Scala中所有的算术运算符其实都是方法,写为简单的函数,它们使用相应的操作符符号作为函数名,并绑定到一个特定类型。

12.练习

练习(1):
scala> def compute(r: Double): Double = { 3.14 * r * r }

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容