详解 Scala 的函数式编程

一、函数基础

函数式是基于数学理论的函数概念,类似于 y = f(x)

1. 函数定义

1.1 语法
/*  函数结构:def funcName(param1 : type1, param2 : type2,...): type = { statement }*/
def sum(x : Int, y : Int): Int = {return x + y
}
1.2 案例
object TestFunctionDefine {def main(args: Array[String]): Unit = {//  函数 1:无参,无返回值def test1(): Unit = {println("1:无参,无返回值")}test1()//  函数 2:无参,有返回值def test2(): String = {return "2:无参,有返回值"}println(test2())//  函数 3:有参,无返回值def test3(s: String): Unit = {println("3:有参,无返回值" + s)}test3("Hi")//  函数 4:有参,有返回值def test4(s: String): String = {return s + "4:有参,有返回值"}println(test4("hello "))//  函数 5:多参,无返回值def test5(name: String, age: Int): Unit = {println(s"${name}今年${age}岁")}test5("阿豪", 18)//  函数 6:多参,有返回值def test6(a: Int, b: Int): Int = {return a + b}println(test6(10, 20))}
}

2. 函数VS方法

  • 概念:

    • 函数:为完成某一功能的程序语句的集合 (区别于函数式编程的函数)
    • 方法:类中的函数
    object TestFunctionAndMethod {def main(args: Array[String]): Unit = {// 定义函数def sayHi(name : String): Unit = {println("hi," + name)}// 函数调用sayHi("张三")// 方法调用TestFunctionAndMethod.sayHi("李四")// 获取方法返回值val result = TestFunctionAndMethod.sayHello("王五")println(result)}// 定义方法def sayHi(name : String): Unit = {println("Hi," + name)}// 定义有返回值的方法def sayHello(name : String): String = {return "Hello," + name}
    }
    
  • 语法:

    • Scala 语言可以在任何的语法结构中声明任何的语法
    • 函数没有重载和重写的概念;方法可以进行重载和重写
    • Scala 中函数可以嵌套定义
    object TestFunctionAndMethod {// 方法可以进行重载和重写,程序可以执行def main(): Unit = {}def main(args: Array[String]): Unit = {// Scala 语言可以在任何的语法结构中声明任何的语法import java.util.Datenew Date()// 函数没有重载和重写的概念,程序会报错def test(): Unit = {println("无参,无返回值")}test()def test(name:String): Unit = {println()}// Scala 中函数可以嵌套定义def test2(): Unit = {def test3(name:String): Unit = {println("函数可以嵌套定义")}}}
    }
    

3. 函数参数

object TestFunctionParam {def main(args: Array[String]): Unit = {// 1.可变参数,且可变参数一般放在参数列表的最后def f1(s: String*): Unit = { // 可变参数底层是 ArrayBuffer[String]println(s)}f1() // WrappedArray()f1("tom") // WrappedArray(tom)f1("tom", "jerry") // WrappedArray(tom, jerry)def f2(s1: String, s2: String*): Unit = {println("s1:" + s1 + ", s2:" + s2)}f2("jack")f2("jack", "rose", "james")// 2.参数默认值,有默认值的参数放置在参数列表的后面def f3(name: String, school: String = "HighSchool"): Unit = {println("my name is " + name + ", my school is " + school)}f3("james")f3("james", "GoodUniversity")// 3.带名参数def f4(name: String = "abc", age: Int): Unit = {println(s"${name}今年已经${age}岁了")}f4("rock", 25)f4(age = 30, name = "curry")f4(age = 18)}
}

4. 函数至简原则

重点

1.4.1 要点
  • return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
  • 如果函数体只有一行代码,可以省略花括号
  • 返回值类型如果能够推断出来,那么可以省略(: 和返回值类型一起省略)
  • 如果有 return,则不能省略返回值类型,必须指定
  • 如果函数明确声明 Unit,那么即使函数体中使用 return 关键字也不起作用
  • Scala 如果期望是无返回值类型,可以省略等号
  • 如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
  • 如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
  • 如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
1.4.2 案例
object TestFunctionSimplify {def main(args: Array[String]): Unit = {// 函数标准语法def f0(name: String): String = {return name}println(f0("jack"))// 简化:// 1. return 可以省略,Scala 会使用函数体的最后一行代码作为返回值def f1(name: String): String = {name}println(f1("jack"))// 2. 如果函数体只有一行代码,可以省略花括号def f2(name: String): String = nameprintln(f2("jack"))// 3. 返回值类型如果能够推断出来,那么可以省略(: 和返回值类型一起省略)def f3(name: String) = nameprintln(f3("jack"))// 4. 如果有 return,则不能省略返回值类型,必须指定/* error:def f4(name: String) = {return name}*/// 5. 如果函数明确声明 Unit,那么即使函数体中使用 return 关键字也不起作用def f5(name: String): Unit = { // 函数返回值为 ()return name // 失效}println(f5("jack")) // ()// 6. Scala 如果期望是无返回值类型,可以省略等号def f6(name: String) { println(name)}println(f6("jack"))// 7. 如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加def f7() { println("jack")}f7()f7// 8. 如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略def f8 { println("jack")}// f8() // 报错f8// 9. 如果不关心名称,只关心逻辑处理,那么def和函数名可以省略,即匿名函数或 lambda表达式(name: String) => { println(name) }}
}

二、函数高级

1. 匿名函数

1.1 介绍
  • 概念:匿名函数是没有名字的函数,也称为 lambda 表达式

  • 语法:

    (param1: type1, param2: type2,...) => { statement }
    
1.2 案例
object TestLambda {def main(args: Array[String]): Unit = {// 单参数案例:val fun = (name: String) => { println(name) }fun("jack")def f(func: String => Unit): Unit = {func("jack")}f((name: String) => { println(name) })// 简化:// 1. 参数的类型可以省略,会根据形参进行自动的推导f((name) => { println(name) })// 2. 类型省略之后,若只有一个参数,则圆括号可以省略,否则不能省略圆括号f( name => { println(name) } )// 3. 匿名函数如果只有一行,则大括号也可以省略f( name => println(name) )// 4. 如果参数只出现一次,则参数省略且后面参数可以用_代替f( println(_) )// 5. 如果方法体只是某个方法的调用,则只需要传入调用的方法名f( println )// 多参数案例:def f2(func: (Int, Int) => Int): Int = {func(1,2)}f2((a: Int, b: Int) => a + b)f2((a: Int, b: Int) => a - b)f2((a, b) => a + b)f2((a, b) => a - b)f2( _ + _ )f2( _ - _ )f2((a, b) => b - a)f2( -_ + _ )}
}
1.3 练习
/**
练习 1:定义一个匿名函数,并将它作为值赋给变量 fun。函数有三个参数,类型分别为 Int,String,Char,返回值类型为 Boolean。要求调用函数 fun(0, “”, ‘0’) 得到返回值为 false,其它情况均返回 true。
练习 2:定义一个函数 func,它接收一个 Int 类型的参数,返回一个函数(记作 f1)。它返回的函数 f1,接收一个 String 类型的参数,同样返回一个函数(记作 f2)。函数 f2 接收一个 Char 类型的参数,返回一个 Boolean 的值。要求调用函数 func(0) (“”) (‘0’)得到返回值为 false,其它情况均返回 true
*/
object Practice {def main(args: Array[String]): Unit = {// 练习 1val fun = (a: Int, b: String, c: Char) => { if(a == 0 && b == "" && c == '0') false else true }println(fun(0, "", '0'))println(fun(1, "", '1'))println("==========================")// 练习 2def func(a: Int): String => (Char => Boolean) = {def f1(s: String): Char => Boolean = {def f2(c: Char): Boolean = {if(a == 0 && s == "" && c == '0') false else true}f2 _}f1}println(func(0)("")('0'))println(func(1)("")('1'))// 练习 2 匿名函数写法def func1(a: Int): String => (Char => Boolean) = {s => c => if(a == 0 && s == "" && c == '0') false else true}println(func1(0)("")('0'))println(func1(1)("")('1'))// 练习 2 柯里化写法(推荐)def func2(a: Int)(s: String)(c: Char): Boolean = {if(a == 0 && s == "" && c == '0') false else true}println(func2(0)("")('0'))println(func2(1)("")('1'))}
}

2. 高阶函数

2.1 特性
  • 函数可以作为值进行传递

    object TestHighLevelFunc {def main(args: Array[String]): Unit = {def f(n: Int): Int = {println("f调用")n + 1}def ff(): Int = {println("ff调用")1}val r1 = f(11) // 返回值val f1 = f _ // 函数 f 整体赋值给函数 f1val f11: Int => Int = f // 函数 f 整体赋值给函数 f1val r2 = ff // 返回值val ff1 = ff _ // 函数 ff 整体赋值给函数 ff1val ff2: () => Int = ff // 函数 ff 整体赋值给函数 ff2}
    }
    
  • 函数可以作为参数进行传递

    object TestHighLevelFunc {def main(args: Array[String]): Unit = {def calculator(func: (Int, Int) => Int, a: Int, b: Int): Int = {func(a, b)}def add(a: Int, b: Int): Int = {a + b}val result = calculator(add, 12, 18)println(result)// 匿名函数println(calculator((a,b)=>a + b, 12, 18))println(calculator(_ + _, 12, 18))}
    }
  • 函数可以作为函数返回值返回

    object TestHighLevelFunc {def main(args: Array[String]): Unit = {def outF(): Int => Unit = {def inF(a: Int): Unit = {println("inF调用 " + a)}// inF _inF // 将函数整体作为返回值返回}val result = outF() // inF 函数的引用地址值println(outF()(10)) // 最终函数调用}
    }
2.2 案例
object FunctionPractice {def main(args: Array[String]): Unit = {val array: Array[Int] = Array(1, 2, 3, 4)// 1. 模拟 Map 映射def mapOP(array: Array[Int], op: Int => Int): Array[Int] = {for(elem <- array) yield op(elem)}def add(elem: Int): Int = {elem + 1}val res1 = mapOP(array, add)val res2 = mapOP(array, _ + 1) // 匿名函数传参println(res1.mkString(","))println(res2.mkString(","))// 2. 模拟 Filter 过滤def filterOP(array: Array[Int], op: Int => Boolean): Array[Int] = {for(elem <- array if op(elem)) yield elem}def filterFun(elem: Int): Boolean = {if(elem % 2 == 0) true else false}val res3 = filterOP(array, filterFun)val res4 = filterOP(array, _ % 2 == 0) // 匿名函数传参println(res3.mkString(","))println(res4.mkString(","))// 3. 模拟 Reduce 聚合def reduceOP(array: Array[Int], op: (Int, Int) => Int): Int = {var init: Int = array(0)for (index <- 1 until arr.length) {init = op(init, arr(index))}init}def multiply(a: Int, b: Int): Int = {a * b}val res5 = reduceOP(array, multiply)val res6 = reduceOP(array, _ * _)println(res5)println(res6)}
}

3. 闭包

如果一个函数,访问到了它的外部(局部)变量的值,那么这个函数和他所处的环境,称为闭包

object TestClosure {def main(args: Array[String]): Unit = {def func(a: Int): String => (Char => Boolean) = {def f1(s: String): Char => Boolean = {def f2(c: Char): Boolean = {if(a == 0 && s == "" && c == '0') false else true}f2 _}f1}       }
}/*在正常情况下,上述嵌套函数在调用的过程中由于外层函数先调用进行压栈和弹栈,所以外层函数的局部变量随着栈空间释放掉,从而导致内层函数调用时无法再获取到外层函数的局部变量,导致执行失败而 scala 的闭包是将外层函数和内层函数整体打包成一个对象存储在堆内存中,从而内层函数在调用时可以从线程共享的堆内存中获取到外层的局部变量正常执行
*/

4. 函数柯里化

  • 概念:把一个参数列表的多个参数,变成多个参数列表

  • 案例:

    object TestCurrying {def main(args: Array[String]): Unit = {// 闭包def add(a: Int): Int => Int = {def f(b: Int): Int = {a + b}f}// 匿名函数简化def add1(a: Int): Int => Int = a + _// 柯里化可以简化闭包写法def add2(a: Int)(b: Int): Int = a + b}
    }
    

5. 递归

  • 概念:一个函数/方法在函数/方法体内又调用了本身,称为递归调用

  • 条件:

    • 方法调用自身
    • 方法必须要有跳出的逻辑
    • 方法调用自身时,传递的参数应该有规律
    • Scala 中的递归必须声明函数返回值类型
  • 案例:

    object TestRecursion {def main(args: Array[String]): Unit = {// 计算阶乘/* 普通递归实现缺点:在调用自身的同时前一次的调用必须等待(不能弹栈),从而导致大量占用Stack空间,进而引发 stack over flow*/def fact(n: Int): Int = {if(n == 0) 1 else fact(n - 1) * n}println(fact(5))/*尾递归优化实现函数式编程语言都是支持尾递归Scala 的注解 @tailrec 标注在方法上可以检验方法尾递归实现是否正确*/def tailFact(n: Int): Int = {@tailrecdef loop(n: Int, currRes: Int): Int = {if(n == 0) currRes else loop(n - 1, currRes * n)}loop(n, 1)}println(tailFact(5))}
    }
    

6. 控制抽象

Java 只有值调用;Scala 既有值调用,又有名调用

object TestControlAbstract {def main(args: Array[String]): Unit = {// 1. 值调用:把函数计算后的值作为参数传递给另一个函数def f0(a: Int): Unit = {println("a: " + a)println("a: " + a)}def f(): Int = {println("f被调用")10}f0(f()) // f 被调用一次println("==========================")// 2. 名调用:将代码块作为参数传递给一个函数// => Int 表示返回值类型为 Int 的代码块 def f1(a: => Int): Int = {println("a: " + a)println("a: " + a)}f1(f()) // f 被调用两次}
}
/*自定义 while 循环
*/
object MyWhile {def main(args: Array[String]): Unit = {// 1. 普通 while 循环var n = 10while(n >= 1) {println(n)n -= 1}// 2. 使用匿名函数、柯里化、闭包、控制抽象和递归实现自定义 while// 解析 while 语法:相当于 func(param1)(param2),param1 和 param2 都是代码块参数def myWhile(condition: => Boolean)(op: => Unit): Unit = {if(condition) {opmyWhile(condition)(op)}}var t = 10myWhile(t >= 1){ // 小括号可以省略println(t)t -= 1}}
}

7. 惰性加载

懒加载

  • 概念:当接收函数返回值的变量 (val) 被 lazy 修饰时,函数的执行将被推迟,直到首次使用该变量时函数才会执行。这样的函数也称为惰性函数。

  • 案例:

    object TestLazyLoad {def main(args: Array[String]): Unit = {// 正常加载val n1: Int = sum(10, 12)println("1. 函数调用")println("2. result: " + n1)println("4. result: " + n1)// 打印顺序:3 > 1 > 2 > 4println("========================")// 惰性加载// lazy 不能修饰 var 类型的变量lazy val n2: Int = sum(11, 12)println("1. 函数调用")println("2. result: " + n2)println("4. result: " + n2)// 打印顺序:1 > 3 > 2 > 4}def sum(a: Int, b: Int): Int = {println("3. sum 调用")a + b}}
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/18586.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于振弦采集仪的岩土工程振弦监测技术研究与应用

基于振弦采集仪的岩土工程振弦监测技术研究与应用 岩土工程振弦监测技术是一种基于振弦采集仪的测试方法&#xff0c;用于对岩土体的力学特性进行监测和分析。振弦采集仪是一种先进的测试设备&#xff0c;能够准确测量岩土体中的振动响应&#xff0c;并通过分析振动信号来获取…

mysql中的内连接与外连接

在MySQL中&#xff0c;内连接和外连接是用于从多个表中检索数据的两种不同的连接方式。 内连接&#xff08;INNER JOIN&#xff09;&#xff1a; 内连接返回两个表之间匹配的行。它只返回两个表中共同匹配的行&#xff0c;如果在一个表中没有匹配到对应的行&#xff0c;则不会显…

学习笔记——数据通信基础——数据通信网络(拓扑结构)

网络拓扑 网络拓扑(Network Topology)是指用传输介质(例如双绞线、光纤等)互连各种设备(例如计算机终端、路由器、交换机等)所呈现的结构化布局。 1、网络拓扑形态 星型网络∶所有节点通过一个中心节点连接在一起。 优点∶容易在网络中增加新的节点。通信数据必须经过中心节点…

python-数据可视化(总)

python-数据可视化 ** 数据可视化 指的是通过可视化表示来探索数据&#xff0c;它与数据挖掘**紧密相关&#xff0c;而数据挖掘指的是使用代码来探索数据集的规律和关联。数据集可以是用一行代码就能表示的小型数字列表&#xff0c;也可以是数以吉字节的数据 最流行的工具之一…

yolox代码详解(从tools/train.py开始扩展,更新中)

一、训练部分相关代码详解 tools/train.py #!/usr/bin/env python3 # -*- coding:utf-8 -*- # Copyright (c) Megvii, Inc. and its affiliates.import argparse import random import warnings from loguru import loggerimport torch import torch.backends.cudnn as cudnn…

HTTP协议的基本了解

一、HTTP-概述 HTTP&#xff1a;Hyper Text Transfer Protocol(超文本传输协议)&#xff0c;规定了浏览器与服务器之间数据传输的规则。 http是互联网上应用最为广泛的一种网络协议 。http协议要求&#xff1a;浏览器在向服务器发送请求数据时&#xff0c;或是服务器在向浏览器…

微信小程序显示流格式照片

1.服务端,java代码,用于将图片转为文件流返回给前端 import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths;import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.ht…

7 步解决Android Studio模拟器切换中文输入

详细步骤传送地址&#xff1a;Android Studio 模拟器切换中文输入 目录 01 问题概述 02 模拟器的调试 01 问题概述 大家在使用Android Studio 软件进行项目演示时总会遇到一些输入框需要输入中文汉字的情况&#xff0c;由于AS自带的模拟器基本都是英文&#xff0c;这时就有同…

Dubbo生态之深度分析sentinel的流量控制

1. 深度了解sentinel限流规则参数的含义 博客Dubbo生态之sentinel限流-CSDN博客中有dubbo集成sentinel的demo演示 在sentinel中&#xff0c;限流的直接表现形式就是&#xff0c;在执行Entry nodeA SphU.entry(resourceName)的时候抛出FlowException异常&#xff0c;FlowExce…

Linux学习笔记(二)

一、Linux文件目录 1.命令&#xff1a;tree -L 1 2.挂载命令&#xff08;例如U盘&#xff0c;需要挂载之后才能访问&#xff09;&#xff1a; mount /dev/cdrom /mnt ls /mnt 3.查看登录信息&#xff1a; last / lastlog 4.修改/查看网络信息 vi /etc/sysconfig/netw…

es索引的性能优化配置

获取所有索引 GET _cat/indices?index* 查看所有模板&#xff0c;如果数据量多的话&#xff0c;可以进行纵向分表&#xff0c;例如按照日期将索引分开&#xff0c;然后通过建模板&#xff0c;配置索引的参数 GET _cat/templates 查看模板详情 GET _template/模板名称 获取集群…

仓库包含 maven { url “https://maven.google.com“ }项目无法正常运行的情况

maven { url "https://maven.google.com" } 在国内是可以使用的&#xff0c;但可能会受到网络环境的影响&#xff0c;导致访问速度较慢或不稳定。为了提高访问速度和稳定性&#xff0c;您可以考虑使用国内的镜像源来替代官方的 Google Maven 仓库。 国内有多个镜像源…

【吊打面试官系列】Java高并发篇 - 死锁的原因?

大家好&#xff0c;我是锋哥。今天分享关于 【死锁的原因&#xff1f;】面试题&#xff0c;希望对大家有帮助&#xff1b; 死锁的原因&#xff1f; 1、是多个线程涉及到多个锁&#xff0c;这些锁存在着交叉&#xff0c;所以可能会导致了一个锁依赖 的闭环。 例如&#xff1a; …

计算机网络7——网络安全1 概述与加密

文章目录 一、网络安全问题概述1、计算机网络面临的安全性威胁2、安全的计算机网络3、数据加密模型 二、两类密码体制1、对称密钥密码体制2、公钥密码体制 随着计算机网络的发展&#xff0c;网络中的安全问题也日趋严重。当网络的用户来自社会各个阶层与部门时&#xff0c;大量…

文件跨境传输法律管控越来越严格,企业该如何有效应对?

文件跨境传输已经成为非常普遍的业务需求&#xff0c;企业在世界各地的总分支处、合作伙伴&#xff0c;客户间开展业务时&#xff0c;必须基于数据的跨境流转而展开。 但随着世界各国对数据安全的重视&#xff0c;文件跨境传输也日趋严格&#xff0c;各国在法律法规上均出具了更…

go语言切片、数组去重函数SliceUnique 支持所有值是可比较类型的切片或者数组去重

我们在go语言开发的时候经常会需要对切片或者数组进行去重操作&#xff0c; 今天就给大家分享一个切片 或者数组去重的通用函数。 这里提醒大家注意go语言是严格区分数据类型的&#xff0c; 切片slice 和 数组 array是不同的数据类型&#xff0c; 不要混淆&#xff01;&#x…

[RK3588-Android12] 关于ES8388 喇叭+PDM回采 4+2配置

问题描述&#xff1a; ES8388 喇叭PDM回采 42配置如下&#xff1a; 解决方案&#xff1a; // MICpdmics: dummy-codec {status "okay";compatible "rockchip,dummy-codec";#sound-dai-cells <0>;};// MICpdm_mic_array: pdm-mic-array {status …

thingsboard接入臻识道闸

thingsboard 和tb-gateway 是通过源码idea启动测试开发 为了测试这里只是买了臻识道闸的摄像机模组方便调试,然后添加一个开关量开关模拟雷达 道闸品牌 臻识C3R3C5R5变焦500万车牌识别相机高速追逃费相机华厦V86像机 淘宝地址 https://item.taobao.com/item.htm?_us1thkikq4…

Python01 注释,关键字,变量,数据类型,格式化输出

# 导入模块 import keyword# 我的第一个Python程序 这是单行注释 快捷键&#xff1a;CTRL/这里是多行注释 可以写多行&#xff0c;用 三个单引号 包起来print(Hello work) print(你好&#xff0c;中国)aa 这是不是注释了&#xff0c;是多行文本。print(aa)# 快速创建 python …

气膜建筑:无硬件支撑的奇迹—轻空间

气膜建筑是一种创新的建筑形式&#xff0c;其独特之处在于其内部没有任何硬件支撑&#xff0c;仅靠空气吹起来。这种技术是如何实现的呢&#xff1f; 气膜结构的原理 气膜建筑的核心在于其充气结构。通过不断向气膜内部充入空气&#xff0c;气膜内部会维持一个较高的气压。这种…