【Scala---04】函数式编程 『 函数 vs 方法 | 函数至简原则 | 函数式编程』

文章目录

  • 1. 函数 vs 方法
    • 1.1 方法
      • (1) 定义方法
      • (2) 运算符即方法
    • 1.2 函数
      • (1) 定义函数
      • (2) 匿名函数
    • 1.3 方法转为函数
    • 1.4 可变参数&默认参数
  • 2. 函数至简原则
  • 3. 函数式编程
    • 3.1 函数式编程思想
    • 3.3 函数柯里化&闭包
    • 3.5 递归 & 尾递归
  • 4. 补充
    • 4.1 访问元祖元素
    • 4.2 =>的含义
    • 4.3 下划线的使用总结

1. 函数 vs 方法

在Java中方法与函数没区别,但是在Scala中方法和函数是不一样的。Java是面向对象的,Scala是面向函数式编程的,主要体现在如下4点:

Java对象可以作为一个值传递对象可以作为参数传递对象可以作为返回值传递可以调用对象
Scala函数可以作为一个值传递函数可以作为参数传递函数可以作为返回值传递可以调用函数
Scala方法不可以作为一个值传递方法不可以作为参数传递方法不可以作为返回值传递可以调用函数

可以看出Scala中函数比方法更加强大,而函数相比于方法最重要的功能就是函数能作为参数传递,也就是说a和b进行的操作不是写死的,而是可变的。

  • 从位置上理解:方法 只能在类中定义,做为类的属性;函数 可以在任何地方定义。
  • 从是否可以重载的角度:方法定义在类中可以实现重载;函数不可以重载。
  • 从运行位置角度:方法是保存在方法区;函数是保存在堆中。

1.1 方法

(1) 定义方法

定义方法语法如下:

object 类名 {
	def 方法名([变量:变量类型,变量:变量类型]):返回值类型 = {
		方法体
	}
}

比如:
在这里插入图片描述

  1. 方法不能作为值传递

    object Main {
        def main(args: Array[String]): Unit = {
            def add(x:Int, y:Int):Int = {
                x + y
            }
            val result = add   // 会报错
            println(result)
        }
    }
    
  2. 方法不能作为参数传递

    object Main {
        def main(args: Array[String]): Unit = {
            // 1. 定义calculate方法
            def calculate(x: Int, y: Int, func: (Int, Int) => Int): Int = { // x与y进行func操作,这个func函数需要指名 形参类型和返回值
                func(x, y)
            }
    
            // 2. 定义add方法
            def add(x:Int, y:Int):Int = {
                x + y
            }
    
            // 3. 函数作为参数传递
            val result = calculate(2, 3, add) // 不会报错,自动转为函数传递
            println(result)
        }
    }
    
  3. 方法不能作为返回值传递

    object Main {
        def main(args: Array[String]): Unit = {
            // 1. 定义getFunc方法
            def getFunc() = { // x与y进行func操作,这个func函数需要指名 形参类型和返回值
                def func(x: Int, y: Int) = {
                    x + y
                } 
                func  // 会报错
            }
            // 2. 得到函数
            println(getFunc())
        }
    }
    
  4. 方法可以直接调用

    object Main {
        def main(args: Array[String]): Unit = {
            def add(x: Int, y: Int) = x + y
            println(add(2, 3)) // 输出:5
        }
    }
    
    

(2) 运算符即方法

  1. 所有的运算符本质上是方法。比如查看Int
    在这里插入图片描述
    可以看到类中定义了很多以运算符命名的方法
    在这里插入图片描述
    因此,以下两种方法是等价的
     val a = 1 + 2
     val a = 1.+(2)
    
  2. 反过来,可以定义这样的方法,并调用
    1. 例子1

      object Cal {
          def say(x:Int):Int = x
      }
      
      object Main {
          def main(args: Array[String]): Unit = {
              // 1. 用 类.方法名(参数列表) 形式调用方法
              var result1 = Cal.say(3)
      
              // 2. 用 类 方法 参数列表 形式调用方法
              var result2 = Cal say (3)  
              var result3 = Cal say 3    // 如果参数列表只有一个值,括号是可以省略的
          }
      }
      
    2. 例子2

      object Main {
      
          def say(x:Int):Int = x
      
          def main(args: Array[String]): Unit = {
              // 1. 用 类.方法名(参数列表) 形式调用方法
              var result1 = this.say(3)
      
              // 2. 用 类 方法 参数列表 形式调用方法
              var result2 = this say (3)  // 如果参数列表只有一个值,括号是可以省略的
              var result3 = this say 3
          }
      }
      

1.2 函数

(1) 定义函数

Scala中有23个函数接口类,函数的本质就是实现这些接口类来实现强大的功能:
在这里插入图片描述

  1. Function0:无参数,无返回值
  2. Function1:…

定义函数语法如下:

object 类名 {
	def 函数名:([变量类型,变量类型])=>返回值类型 = ([变量:变量类型,变量:变量类型]) => {
		方法体
	}
}

比如:
在这里插入图片描述

  1. 函数可以作为一个值传递

    object Main {
        def main(args: Array[String]): Unit = {
            val add = (x:Int, y:Int) => x + y
            val result = add   // 不会报错
            println(result) 
        }
    }
    
  2. 函数可以作为参数传递,此时需要指名函数的类型。格式为:(参数类型)=>返回值类型

    object Main {
        def main(args: Array[String]): Unit = {
            // 1. 定义calculate方法
            def calculate(x:Int, y:Int, func:(Int, Int)=>Int): Int = { // x与y进行func操作,这个func函数需要指名 形参类型和返回值
                func(x, y)
            }
            // 2. 定义add函数
            def add = (x:Int, y:Int) => x + y
            // 3. 函数作为参数传递
            val result = calculate(2, 3, add)
            // 4. 输出
            println(result)  // 输出5
        }
    }
    
  3. 函数可以作为返回值传递

    object Main {
        def main(args: Array[String]): Unit = {
            // 1. 定义getFunc方法
            def getFunc() = { // x与y进行func操作,这个func函数需要指名 形参类型和返回值
                val func = (x:Int, y:Int) => x + y
                func
            }
            // 2. 得到函数
            println(getFunc())
            // 3. 得到函数并执行函数
            println(getFunc()(2, 3))  // 输出5
        }
    }
    
  4. 函数可以执行

    object Main {
        def main(args: Array[String]): Unit = {
            val add = (x:Int, y:Int) => x + y
            println(add(2, 3))
        }
    }
    

(2) 匿名函数

匿名函数与函数的语法区别就在于前者是val关键字,后者是def关键字。比如:
在这里插入图片描述
匿名函数简化了函数值传递:
在这里插入图片描述

注意:这只会定义一个函数,也不会执行函数这点和Java不一样,java的lambda表达式是会创建对象,并执行构造方法。

1.3 方法转为函数

  1. 通用转换方式,在方法名后面 + 空格 + _ 即可,语法为:方法名 _

    object Main {
        def main(args: Array[String]): Unit = {
            def add(x:Int, y:Int):Int = {
                x + y
            }
            val result = add _     // 将方法转为函数
            println(result(2, 3))  // 输出:5
        }
    }
    
  2. 在参数处将方法转为函数时,可以简化,去掉 _,直接方法名

    object Main {
        def main(args: Array[String]): Unit = {
            // 1. 定义calculate方法
            def calculate(x: Int, y: Int, func: (Int, Int) => Int): Int = { // x与y进行func操作,这个func函数需要指名 形参类型和返回值
                func(x, y)
            }
    
            // 2. 定义add函数
            def add(x:Int, y:Int):Int = {
                x + y
            }
    
            // 3. 函数作为参数传递
            val result1 = calculate(2, 3, add _)      // 此时可优化,去掉 空格_
            val result2 = calculate(2, 3, add)  // 优化后的代码
        }
    }
    

1.4 可变参数&默认参数

object Test03_FunArgs {
    def main(args: Array[String]): Unit = {

        // (1)可变参数:在类型后面加*号
        def sayHi(names:String*):Unit = {
            println(s"hi $names")
        }

        sayHi()
        sayHi("linhai")
        sayHi("linhai","jinlian")

        // (2)可变参数必须在参数列表的最后
        def sayHi1(sex: String,names:String*):Unit = {
            println(s"hi $names")
        }

        // (3)参数默认值
        def sayHi2(name:String = "linhai"):Unit = {
            println(s"hi ${name}")
        }

        sayHi2("linhai")
        sayHi2()


        // (4)默认值参数在使用的时候 可以不在最后
        def sayHi3( name:String = "linhai" , age:Int):Unit = {
            println(s"hi ${name}")
        }

        // (5)带名参数:指调用方法时,指定传参顺序
        sayHi3(age = 10, name = "niu")
    }
}

2. 函数至简原则

在这里插入图片描述

注意:方法最后一行的return可以省略,但是除此之外都不能省略。比如
在这里插入图片描述

object Test04_FuncSimply {

  def main(args: Array[String]): Unit = {

    //(1)return可以省略,Scala会使用方法体的最后一行代码作为返回值
    def func1(x: Int, y: Int): Int = {
		x + y
    }

    // (2)如果方法体只有一行代码,可以省略花括号
    def func2(x: Int, y: Int): Int = x + y

    //(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
    //     此时,函数就变为了数学表达式的形式:f(x, y) = x + y
    def func3(x: Int, y: Int) = x + y

    //(4)如果有return,则不能省略返回值类型,必须指定
    def func4(x: Int, y: Int): Int = {
    	if (x < 20) {
    		return x + y
    	}
    	2 * x + 3 * y
    }

    //(5)如果方法明确声明unit,那么即使函数体中使用return关键字也不起作用
    def func5(x: Int, y: Int): Unit = return x + y

    //(6)Scala如果期望是无返回值类型,可以省略等号
    def func6(x: Int, y: Int) {
		println(x + y)
    }

    // (7)如果方法无参,但是声明了参数列表,那么调用时,小括号,可加可不加
    def func7(): Unit = {
      println("hello")
    }

    // (8)如果方法没有参数列表,那么小括号可以省略,调用时小括号必须省略
    def func8 {
		println("hello")
    }

	// (9)如果不关心函数名,只关心映射逻辑,就会变为lambda表达式
	(x:Int, y:Int) => {println(x + y)}
	val func = (x:Int, y:Int) => {println(x + y)}  // 如果要设置函数名可以这样。此时,func就是函数名
  }
}

3. 函数式编程

3.1 函数式编程思想

  1. 函数式编程思想:① 当做数学题,y = f(x),重要的是映射关系。② 使用val,在分布式上计算后不会产生歧义
  2. 通过前面可以知道,定义函数有3种形式:
    1. 方法转函数。先定义方法,再方法名 _
    2. 直接定义函数def
    3. 匿名函数
      在这里插入图片描述

一看到 => 或 一看到 _,就要想到这是表示函数。

3.3 函数柯里化&闭包

  • 闭包:内层函数用到了外层函数变量,如果直接调用内层函数会取不到外层函数的这个变量值。此时,内层函数(万物皆对象,函数也是对象)的堆中的对象会保留一份引用到外层函数的值。
    闭包参考链接

  • 函数柯里化:将一个接收多个参数的函数转化成一个一个接受参数的函数过程,可以简单的理解为一种特殊的参数列表声明方式。函数柯里化

    object TestFunction {
    	
      val sum = (x: Int, y: Int, z: Int) => x + y + z
    	
      // 函数柯里化的底层逻辑:本质是将函数作为返回值
      val sum1 = (x: Int) => {
        y: Int => {  // 匿名函数
          z: Int => { // 匿名函数
            x + y + z
          }
        }
      }
    
      // 函数柯里化的另一种简单表达
      val sum2 = (x: Int) => (y: Int) => (z: Int) => x + y + z
    
      // 方法也有函数柯里化
      def sum3(x: Int)(y: Int)(z: Int) = x + y + z
      
      def main(args: Array[String]): Unit = {
        sum(1, 2, 3)
        sum1(1)(2)(3) // sum1(1)调用完后,返回一个函数; sum1(1)(2)是调用返回的函数; .......
        sum2(1)(2)(3)
        sum3(1)(2)(3)
      }
    }
    

3.5 递归 & 尾递归

  1. 递归与Java中的递归一样:前面知道scala的方法返回值是可以省略的,默认分配返回值类型,但是 如果方法是递归方法,则必须指定方法的返回值类型,否则会报错

    object Test{
        def main(args: Array[String]): Unit = {
            // 实现阶乘
            def fact(n : Int) : Int = {  // 必须指名方法的返回值类型
                // 跳出递归
                if(n == 1) return 1
                // 递归逻辑
                n * fact(n - 1)
            }
            // 调用阶乘方法
            println(fact(5))
        }
    }
    
  2. 尾递归:递归是将每次调用函数/方法会压入到栈中,是累计使用资源,容易造成栈溢出;而尾递归是覆盖使用资源,不会造成栈溢出。所以,尾递归资源利用率更加高。尾递归参考链接

    一般支持函数式编程语言都支持尾递归;但是Java不支持尾递归。

4. 补充

4.1 访问元祖元素

变量名._数字    
比如:x._1 表示访问x的第一个元素

4.2 =>的含义

https://blog.csdn.net/qq_43546676/article/details/130992479

4.3 下划线的使用总结

https://blog.csdn.net/qq_43546676/article/details/130874779

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

TCP 连接,一端断电和进程崩溃有什么区别?

TCP 连接&#xff0c;一端断电和进程崩溃有什么区别&#xff1f; 前言主机崩溃进程崩溃有数据传输的场景客户端主机宕机&#xff0c;又迅速重启客户端主机宕机&#xff0c;一直没有重启 总结 前言 有的小伙伴在面试腾讯的时候&#xff0c;遇到了这么个问题&#xff1a; 这个属…

一键审计 web 日志(teler)

在 web 系统遭受攻击之后&#xff0c;通常要审计 web 日志来寻找蛛丝马迹&#xff0c;那么有没有可以满足需求的自动化工具呢&#xff1f;今天就来尝试一款开源工具 teler&#xff0c;项目地址&#xff1a; https://github.com/kitabisa/teler/ 先来看一张作者测试图&#xff1…

NPDP|传统行业产品经理如何跨越鸿沟,从用户角度审视产品

随着科技的飞速发展和互联网的普及&#xff0c;产品经理的角色已经从单纯的产品规划者逐渐转变为全方位的用户体验设计者。对于传统行业的产品经理来说&#xff0c;这是一个挑战与机遇并存的时代。他们不仅要面对激烈的市场竞争&#xff0c;还要学会如何跨越与新兴科技行业之间…

一行Python代码可以做什么,超出你想象

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 揭秘编程语言的灵活性 在编程的世界里&#xff0c;简洁就是力量。Python以其优雅和简洁而著称&#xff0c;让开发者能够用更少的代码做更多的事。但这并不意味着功能上的妥协——Python的强大之处在于它允许在一行代…

【基于 PyTorch 的 Python 深度学习】5 机器学习基础(3)

前言 文章性质&#xff1a;学习笔记 &#x1f4d6; 学习资料&#xff1a;吴茂贵《 Python 深度学习基于 PyTorch ( 第 2 版 ) 》【ISBN】978-7-111-71880-2 主要内容&#xff1a;根据学习资料撰写的学习笔记&#xff0c;该篇主要介绍了单 GPU 加速和多 GPU 加速&#xff0c;以及…

今年做电商,视频号小店绝对是明智之举,未来风口就在这里

大家好&#xff0c;我是电商笨笨熊 电商一直是近几年的热门创业方向&#xff1b; 但是面对众多电商平台&#xff0c;对于普通玩家的我们来说&#xff0c;该怎么选择呢&#xff1f; 今年来说&#xff0c;我会更愿意选择视频号小店。 作为一个腾讯推出的电商项目&#xff0c;…

LeetCode例题讲解:移动044

给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12] 输出: [1,3,12,0,0] 示例 2: 输入: nums [0] 输出…

【STM32+HAL】DS18B20读取环境温度

一、准备工作 有关CUBEMX的初始化配置&#xff0c;参见我的另一篇blog&#xff1a;【STM32HAL】CUBEMX初始化配置 二、所用工具 1、芯片&#xff1a; STM32F407VET6 2、IDE&#xff1a; MDK-Keil软件 3、库文件&#xff1a;STM32F4xxHAL库 三、实现功能 串口打印当前温度值…

Day_3

1. HttpClient HttpClient是Apache的一个子项目&#xff0c;是高效的、功能丰富的支持HTTP协议的客户端编程工具包 作用&#xff1a;发送HTTP请求&#xff0c; 接受相应数据 <dependency><groupId>org.apache.httpcomponents</groupId><artifactId>…

Deep Learn Part Six Gated RNN-24.5.1

本章核心一句话&#xff1a; 卸下包袱&#xff0c;轻装上阵。--尼采 总述&#xff1a;本章所学内容 0.引子&#xff1a; 上一章介绍的 RNN 之所以不擅长学习时序数据的长期依赖关系&#xff0c;是因为 BPTT 会发生梯度消失和梯度爆炸的问题。本节我们将首先回顾一下上一章介…

21物联1班shift五次

1.选择推荐选项 2.等待 3.点击取消 4.选择查看问题详细信息 5.点击txt文件 6.找到system文件夹&#xff0c;将sethc改为qqq&#xff0c;将cmd文件改为sethc文件 7.单击完成。重新启动虚拟机。连续按五次shift出现cmd框&#xff0c;修改密码

MySql#MySql安装和配置

目录 一、卸载不需要的环境 二、安装mysql yum 源 三、开始安装 四、如果保证安装成功呢&#xff1f; 五、MySql 启动&#xff01; 六、登录mysql 七、配置文件说明 八、设置开机启动&#xff01; 本次安装是在Linux环境在centos7中完成 首先先将自己切换成root 一、…

彻底搞懂大小端存储and调试中内存窗口如何使用?

定义 首先我们有一个常识&#xff0c;Windows采用小端存储方式。 探究Windows下vs2019是什么存储&#xff1f; 在小端存储方式中&#xff0c;低字节存储在内存的低地址处&#xff0c;高字节存储在内存的高地址处。这与大端存储方式恰好相反&#xff0c;大端存储方式中高字节存…

[图解]DDD领域驱动设计浮夸,Eric Evans开了个坏头

0 00:00:00,630 --> 00:00:02,790 今天我们要讲的是 1 00:00:03,930 --> 00:00:07,420 DDD领域驱动设计浮夸 2 00:00:07,700 --> 00:00:10,590 Eric Evans开了个坏头 3 00:00:14,790 --> 00:00:17,380 在《领域驱动设计》的 4 00:00:18,650 --> 00:00:22,59…

QT:小项目:登录界面 (下一章连接数据库)

一、效果图 登录后&#xff1a; 二、项目工程结构 三、登录界面UI设计 四主界面 四、源码设计 login.h #ifndef LOGIN_H #define LOGIN_H#include <QDialog>namespace Ui { class login; }class login : public QDialog {Q_OBJECTpublic:explicit login(QWidge…

暴露自己IP地址有什么危险

暴露自己的IP地址确实存在一定的危险性&#xff0c;以下是关于这一问题的详细探讨&#xff1a; 一、IP地址的重要性 IP地址是互联网通信中的关键标识&#xff0c;它使得网络中的设备能够相互识别并进行数据传输。在网络世界中&#xff0c;每台设备都需要一个独特的IP地址来确…

2024蓝桥杯CTF writeUP--packet

根据流量分析&#xff0c;我们可以知道129是攻击机&#xff0c;128被留了php后门&#xff0c;129通过get请求来获得数据 129请求ls Respons在这 里面有flag文件 这里请求打开flag文件&#xff0c;并以base64编码流传输回来 获得flag的base64的数据 然后解码 到手

C语言 举例说明循环嵌套

今天 我们来说循环的嵌套 如果一个循环体内 又包含了另一个循环结构 我们称之为循环的嵌套 我们之前学的 While do-while for 都可以进行相互的嵌套 如下图 在 While 循环语句中再嵌套一个 While 循环语句 do-while 中嵌套 do-while for中嵌套 for 例如 我们做一个九九乘法…

mysql中varchar与bigint直接比较会导致精度丢失以至于匹配到多行数据

在mysql中&#xff0c;我们都知道如果一个索引字段使用了函数或者计算那么查询的时候索引会失效&#xff0c;可是我相信在联表的时候我们只会关注两个表关联字段是否都创建了索引&#xff0c;却没有关注过这两个字段的类型是否一致&#xff0c;如果不一致的话索引是会失效的&am…

Redis 实战3

系列文章目录 本文将从跳跃表的实现、整数集合来展开 Redis 实战 Ⅲ 系列文章目录跳跃表的实现跳跃表节点层 前进指针跨度 整数集合的实现升级升级的好处提升灵活性节约内存 降级整数集合 API总结 跳跃表的实现 Redis 的跳跃表由 redis.h/zskiplistNode 和 redis.h/zskiplist…
最新文章