Scala函数式编程【从基础到高级】

news2024/12/23 19:07:35

目录

函数基础

基本语法

函数参数

函数至简原则

至简原则细节

 函数高级

高阶函数

函数的3种高阶用法:

1、函数作为值传递

2、函数作为参数传递

匿名函数作参数传递

3、函数作为返回值传递

匿名函数的至简规则

高阶函数的应用 

案例-将数组中的元素全部+1后返回

 匿名函数

练习1-匿名函数作为值传递:

练习2-函数作为返回值

 函数柯里化&闭包

案例2的闭包实现:

案例2的柯里化实现:

闭包案例 - 常量a+变量b

案例 - 柯里化

递归

斐波那契数列

阶乘 

尾递归

Scala实现尾递归求阶乘 

Java实现尾递归求阶乘

控制抽象

传值调用

传名调用

应用案例 - 自定义函数实现while  

惰性加载


函数基础

        Scala 的函数式编程以及面向对象的特点,使它能够很好的应用到大数据场景下,比如 Spark、Kafka 的底层都是 Scala 编写的。

基本语法

        在Java中,方法只能够在类下面声明,不可以在main方法以及其它方法内部声明,但是Scala可以,因为Scala是一个函数式编程,函数是一等公民。

        所以,我们通常把main方法内部定义的方法叫做函数,main外部声明的方法才叫做对象的方法。

object Test{

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

    //定义函数
    def sayHello(name: String): Unit = {
      println("hello " + name)
    }

    //调用函数
    sayHello("GGBond")

    //调用对象的方法
    Test01_Function.sayHello("Tom")
  }

  //定义对象的方法
  def sayHello(name: String): Unit = {
    println("hello " + name)
  }
}

        可以看到,我们可以直接通过函数名调用函数,但是在伴生类中,一切类变量和方法都是静态的,所以我们需要通过类名才可以调用方法。

函数参数

  1. 可变参数
  2. 如果参数列表中存在多个参数,那么可变参数一般放置在最后
  3. 参数默认值,一般将有默认值的参数放置在参数列表的后面
  4. 带名参数
def main(args: Array[String]): Unit = {

    //1.可变参数
    def f1(names: String*): Unit = {
      println(names)
    }
    f1("alice","tom","GG bond")

    //2.如果参数列表中存在多个参数,那么可变参数一般放置在最后
    def f2(grade: String, names: String*): Unit = {
      println(grade)
      println(names)
    }
    f2("大二","alice","tom","GG bond")

    //3.参数默认值,一般将有默认值的参数放置在参数列表的后面
    def f3(name: String = "GG bond"): Unit = {
      println(name)
    }
    f3()
    f3("燕双鹰")

    //4.带名参数
    def f4(name: String, age: Int): Unit = {
      println(s"${name}的年龄=${age}")
    }
    f4("GG bond",14)
    f4(age = 13,name = "熊大")
  }

函数至简原则

代码能省则省

至简原则细节

  • return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
  • 如果函数体只有一行代码,可以省略花括号
  • 返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
  • 如果有 return,则不能省略返回值类型,必须指定
  • 如果函数明确声明 unit,那么即使函数体中使用 return 关键字也不起作用
  • Scala 如果期望是无返回值类型,可以省略等
  • 如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
  • 如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
  • 如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
object Test{

  def main(args: Array[String]): Unit = {
    //(1)return 可以省略,Scala 会使用函数体的最后一行代码作为返回值
    def f0(name: String): String = {
      return name
    }
    println(f0("GG bond"))

    def f1(name: String): String = {
      name
    }
    println(f1("GG bond"))
    //(2)如果函数体只有一行代码,可以省略花括号
    def sum(a: Int,b: Int): Int = a+b

    //(3)返回值类型如果能够推断出来,那么可以省略(:和返回值类型一起省略)
    def add(a: Int,b: Int) = a+b  //因为参数都是Int类型,所以可以自己推断返回值类型

    //(4)如果有 return,则不能省略返回值类型,必须指定
    def f3(name: String): String = {
      return name
    }


    //(6)Scala 如果期望是无返回值类型,可以省略等号
    def f4(name: String){
      println("hello")
    }

    //(7)如果函数无参,但是声明了参数列表,那么调用时,小括号,可加可不加
    def f5() = "name"
    println(f5)

    //(8)如果函数没有参数列表,那么小括号可以省略,调用时小括号必须省略
    def f6 = "hi"

    //(9)如果不关心名称,只关心逻辑处理,那么函数名(def)可以省略
    def say_Hi(name: String): Unit = {
      println("hi" + name)
    }
    //匿名函数 匿名函数的返回值是一个函数
    val say_Hello = (name: String) => {"hello " + name}
    //调用匿名函数
    println(say_Hello("GG bond"))


  }

}

 函数高级

高阶函数

        高阶函数指的是接受一个或多个函数作为参数,并返回一个函数作为结果的函数。函数是Scala中的一等公民,地位很高,这体现在它可以在main方法中直接定义并通过函数名来直接使用,而不用向Java一样,需要再对象下面声明方法,来通过对象或者类名来调用。高阶函数可以减少代码的重复,并增加代码的灵活性和可重用性。

        通常我们的业务数据在高阶函数中写死,等待传进来的函数参数来作处理。

函数的3种高阶用法:

  1. 函数作为值传递
  2. 函数作为参数传递
  3. 函数作为返回值

1、函数作为值传递

        函数作为返回值赋值给一个变量,这个变量类型是一个函数对象。如果直接输出的话,如果这个函数没有参数,将直接输出这个函数的返回值,否则直接输出这个对象的地址(对象引用)。我们也可以通过返回值来直接使用,因为返回值本身就是一个函数对象,我们可以直接+(参数)来调用函数。

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

    //定义一个普通函数 输入num返回num+1
    def f(num: Int): Int = {
      num + 1
    }

    //1.函数作为值传递 -两种方式返回函数对象
    val f1: Int => Int = f
    val f2 = f _

    //我们可以直接输出函数对象,也可以直接利用函数对象进行使用传递参数
    println(f1)   //输出这个对象的地址(也叫对象引用)
    println(f1(9))
    println(f2)   //输出这个对象的地址(也叫对象引用)
    println(f2(10))

    //定义一个函数无参,直接返回1
    def fun(): Int = {
      1
    }

    val f3 = fun    //因为fun是无参的,所以这里返回的是返回值 1
    val f4 = fun _  //返回函数体
    val f5: () => Int = fun

    println(f3)   //输出 1
    println(f4)   //输出这个对象的地址(也叫对象引用)
    println(f5)   //输出这个对象的地址(也叫对象引用)

  }

2、函数作为参数传递

  我们可以定义两个函数,分别实现两个数的加和减,然后我们可以将这两个函数作为参数传递给另一个函数,来实现对两个数的处理(数据是写死的)。

    //定义一个函数 sum 求和
    def sum(a: Int,b: Int): Unit = {
      println(s"${a} + ${b} = ${a+b}")
    }

    //定义一个函数 sub 求差,函数体只有一行可以省去花括号{}
    def sub(a: Int,b: Int): Unit = println(s"${a} - ${b} = ${-b}")

    def fun(f: (Int,Int) => Unit):Unit = {
      f(1,2)
    }

    fun(sum)    //输出 1 + 2 = 3
    fun(sub)    //输出 1 - 2 = -1

        前面我们数据是写死的,也就是说,我们的高阶函数只能够对 1和2进行处理,显然很没用!这里我们直接将要计算的数据页当做参数传递进来。

def doEval(f: (Int,Int) => Int,a: Int ,b: Int): Int = {
      f(a,b)
    }

    def add(a: Int,b: Int): Int = {
      a + b
    }
    //使用普通函数作为参数
    doEval(add,1,2)
    //使用匿名函数做参数
    doEval((a,b) => a + b,1,2)
    //简化代码
    doEval(_ + _,1,2)
匿名函数作参数传递

我们可以直接将匿名函数作参数传入fun,更加简化代码。

//以函数作为参数
    def fun(f: (Int,Int) => Unit): Unit = f(1,2)

    //直接传入匿名函数
    fun((a: Int,b: Int) => println(s"${a} + ${b} = ${a+b}")))   //输出 1 + 2 = 3
    //高阶函数定义了函数参数的类型,匿名函数这里就不需要再指定函数类型了
    fun((a,b) => println(s"${a} - ${b} = ${a-b}")))   //输出 1 - 2 = -1

3、函数作为返回值传递

函数作为返回值后返回函数对象,我们可以通过函数对象来使用该函数。

//3.函数作为返回值传递
    def f6(): Int => Unit = {
      def f7(a: Int): Unit = {
        println("f7被调用 " + a)
      }
      f7  //返回函数对象
    }

    //获取f7函数对象 并使用
    val res  = f6()
    //输出:
    //  f7被调用 10
    //  ()
    //  因为f7的返回值为 Unit类型
    println(res(10))

匿名函数的至简规则

  1. 参数的类型可以省略,会根据形参进行自动的推导
  2. 类型省略之后,发现只有一个参数,则圆括号可以省略;其他情况:没有参数和参 数超过 1 的永远不能省略圆括号。
  3. 匿名函数如果只有一行,则大括号也可以省略
  4. 如果参数只出现一次,则参数省略且后面参数可以用下划线 _ 代替
    //定义一个函数,以函数作参数
    def say_Name(f: String => Unit): Unit = f("GG Bond")
    
    //普通调用
    say_Name((name: String) => {println(name)})
    
    //1.参数的类型可以省略,会根据形参进行自动的推导
    say_Name( (name) => {println(name)})
    
    //2.如果参数只有一个 括号省略
    say_Name( name => {println(name)} )

    //3.匿名函数如果只有一行,则大括号可以省略
    say_Name( name => println(name) )

    //4.如果参数只出现一次,则参数省略且后面参数可以用_代替
    say_Name( println(_) )

    //5.如果可以推断出当前传入的println()是一个函数体,而不是调用语句,可以直接省略_
    say_Name( println )

高阶函数的应用 

高阶函数对集合的处理更加体现了Sala对于处理大数据的优势。

案例-将数组中的元素全部+1后返回

val arr: Array[Int] = Array(1,2,4,3,8)

    //对数组进行处理,把对数组的操作进行抽象

    /**
     *
     * @param array 待处理的数组
     * @param op 操作函数(Int => Int)将数组中的数处理后变为另一个数
     * @return 返回新数组
     */
    def arrayOperation(array: Array[Int], op: Int => Int): Array[Int] = {
      for (elem <- array) yield op(elem)
    }

    //定义操作函数
    def addOne(elem: Int): Int = elem + 1

    //调用函数
    val res_arr: Array[Int] = arrayOperation(arr,addOne)

    //用逗号间隔输出新数组的内容
    println(res_arr.mkString(","))

    //使用匿名函数实现
    val res2_arr = arrayOperation(arr,elem => elem + 1)
    //这里的后面的elem可以用下划线代替 _
    val res2_arr = arrayOperation(arr,elem => _ + 1)
    println(res2_arr.mkString(","))

 匿名函数

前面已经用过了,这里在详细解释一下。

说明:没有函数名的函数就是匿名函数。

表达式:(a: Int,b: Int)=>{函数体}

注意匿名函数的定义没有返回值类型,但是可以作为高阶函数的参数时指定

//以函数作为参数
    def fun(f: (Int,Int) => Int): Unit = f(1,2)

    //直接传入匿名函数
    fun((a: Int,b: Int) => a + b ))   //返回 3
    //高阶函数中指定了参数函数的参数类似,所以这里可以省去匿名函数的参数类型
    fun((a,b) => a - b ))   //返回-1

    //如果每个参数只出现一次,可以这样写
    fun( _ + _)    //对应 a + b
    fun( _ - _)    //对应 a - b
    //如果要实现 b-a 可以写作 -a+b
    fun((a,b) => -a + b)    
    fun( -_ + _)    //对应 -a + b

练习1-匿名函数作为值传递:

定义一个匿名函数,并将它作为值赋给变量 fun。函数有三个参数,类型分别为 IntStringChar,返回值类型为 Boolean

要求:调用函数 fun(0, “”, ‘0’)得到返回值为 false,其它情况均返回 true

//匿名函数在没被高阶函数指定返回值类型的时候,需要声明参数名和类型
    val fun = (a: Int,b: String,c: Char) => {
      if (a == 0 && b == "" && c == '0')
        false
      else
        true
    }

    println(fun(1,"",'0'))    //true

练习2-函数作为返回值

        定义一个函数 func,它接收一个 Int 类型的参数,返回一个函数(记作 f1)。 它返回的函数 f1,接收一个 String 类型的参数,同样返回一个函数(记作 f2)。函数 f2 接 收一个 Char 类型的参数,返回一个 Boolean 的值。

        要求调用函数 func(0) (“”) (‘0’)得到返回值为 false,其它情况均返回 true

//表示返回一个输入类型为String输出类型为(Char => Boolean)的函数
def func(a: Int): String => (Char => Boolean) = {
      //表示返回一个输入类型为Char输出类型为Boolean的函数
      def f1 (b: String): Char => Boolean = {
        //表示返回一个返回值类型为Boolean的值
        def f2(c: Char): Boolean = {
          if (a == 0 && b == "" && c == '0') false else true
        }
        f2
      }
      f1
    }

    println(func(0)("")('0'))    //false

使用匿名函数简写

 函数柯里化&闭包

  • 闭包:如果一个函数 ,访问到了它外部变量的值,那么这个函数和它所处的环境,称为闭包
  • 函数柯里化:把一个参数列表的多个参数,变成多个参数列表。

        我们学习闭包和柯里化是为了更加简洁地实现调用上层函数的参数,因为在上面的案例2中,我们通过嵌套函数来实现读取上层函数的参数显然代码很复杂,所以我们可以使用柯里化来实现。

案例2的闭包实现:

//匿名函数简写
    def func1(a: Int): String => (Char => Boolean) = {
      //外层指定了返回值类型 内层的匿名函数就不需要指定参数类型
      b => c => if (a == 0 && b == "" && c == '0') false else true
    }

    
    println(func(1)("")('0'))    //tue

案例2的柯里化实现:

//柯里化
    def func2(a: Int)(b: String)(c: Char): Boolean = {
      if (a == 0 && b == "" && c == '0') false else true
    }

//调用
    println(func2(0)("")('0'))    //true

 我们可以看到,柯里化更加简单,其实柯里化的底层就是闭包来实现的。

闭包案例 - 常量a+变量b

        这并不是一个简单的两数相加,因为实际开发中,我们可能遇到大量的变量对常量不断运算,比如大量数字分别对4相加、对5相加,这就需要定义两个函数addByFour、addByFive;但是如果常量有几百种呢,这就需要定义几百个函数,显然过于复杂。所以这就需要使用双层嵌套函数,以返回值为函数:第一层函数输入常量,第二层函数输入变量。

//A是任意常量
    def addByA(a: Int): Int=>Int = {
      def addB(b: Int): Int = {
        a + b
      }
      addB
    }
    
    //不断简化代码
    
//既然直接返回函数 我们直接定义一个匿名函数即可 scala默认返回最后一行(一个方法可以看作一行)作为返回值
    def addByA1(a: Int): Int=>Int = {
      (b: Int) => a + b
    }
    //指定外层函数指定了返回值类型 内层就不需要写输入类型了
    def addByA2(a: Int): Int=>Int = b => a + b

    //内层的变量 b 只在函数体出现了一次 所以可以用 _ 替换
    def addByA3(a: Int): Int=>Int = a + _

     //调用
    val fun = addByA3(5)    //接收一个函数(任意数+5)作为返回值 
    println(fun(10))    //调用函数 输出15

案例 - 柯里化

        在纯粹的函数式编程中其实是不存在多参数这样的定义的,只是Scala为了兼容其它语言才不做限制的。所以引入柯里化来实现这样的一个效果,将一个含多个参数的参数列表转变为多个仅含一个参数的参数列表。

        柯里化的底层就是闭包。

    //柯里化 底层是闭包实现分层调用
    def addCurrying(a: Int)(b: Int): Int = a + b
    //调用
    println(addCurrying(4)(36))

递归

斐波那契数列

def fb(i: Int): Int = {
      if (i ==1 || i == 2)
        1
      else
        fb(i-1) + fb(i+2)
    }

阶乘 

def jieC(n: Int): Int = {
      if (n==1)
        1
      else
        jieC(n-1)*n

    }

尾递归

        我们知道,在做递归的时候,我们其实是方法不断调用,比如上面的阶乘案例,当我们希望得到5的阶乘时,函数的调用会不断压栈,到达阶乘的最小值1的时候原路返回。显然如果是10亿的阶乘,肯定会造成栈溢出的问题。

 

        所以这就需要引入尾递归的概念,上面的普通递归需要不断压栈最后出栈,比如5的阶乘,它的返回值是 1 -> 2*1 -> 3*2*1 -> 4*3*2*1 -> 5*4*3*2*1

        而我们的尾递归刚好相反,它的返回值是 5 -> 5*4 -> 5*4*3 -> 5*4*3*2 -> 5*4*3*2*1,这样,在调用函数时,不会创建新的栈帧,而是直接复用当前栈帧,因此占用的栈资源是固定的。

Scala实现尾递归求阶乘 

def tailJieC(n: Int): Int = {
      def loop(n: Int,tmp: Int): Int = {
        if (n==0)
          return tmp
        loop(n - 1,tmp * n)
      }
      loop(n,1)
    }

Java实现尾递归求阶乘

public static int JC(int n){
        return loop(n,1);
    }
    public static int loop(int n,int tmp){
        if (n==0){
            return tmp;
        }else {
            return loop(n-1,tmp*n);
        }
    }

控制抽象

传值调用

参数类型:普通参数

println("=====传值调用======")
    //1.传值参数
    def f0(a: Int): Unit = {
      println("a = " + a)
    }

    //数值做参数
    f0(1)

    def f1(): Int = {
      println("f1被调用")
      2
    }

    //函数返回值做参数
    f0(f1())

输出结果

1
2

 

传名调用

参数类型:传递的不再是具体的值,而是代码块


    println("=====传名调用======")
    def f1(): Int = {
      println("f1被调用")
      2
    }

    //2.传名参数
    //f2的参数是代码块 要求代码块的返回值是Int
    def f2(a: => Int): Unit = {
      println("a = " + a)
      println("a = " + a) //a被调用几次 代码块就被执行几次
    }

    f2(23)    //输出两个23

    f2(f1())  //执行两次f1() 相当于把f2的函数体中的 a 都替换做 f1() 这也说明传名调用传递的是代码块而不是返回值

   
    f2({
      println("这是一个代码块")
      20
    })

输出结果

=====传名调用======
a = 23
a = 23
f1被调用
a = 2
f1被调用
a = 2
这是一个代码块
a = 20
这是一个代码块
a = 20

应用案例 - 自定义函数实现while  

//1.常规的while循环
    var n = 10
    while (n > 0){
      println(n)
      n -= 1
    }

    //2.自定义函数实现while
    // 用闭包实现一个函数,将代码块作为参数传入,递归调用
    def myWhile(condition: =>Boolean): (=>Unit)=>Unit = {
      //内层实现递归 参数就是循环体-代码块
      def doLoop(op: => Unit): Unit= {
        if (condition){
          op
          myWhile(condition)(op)
        }
      }
      doLoop _
    }
    //给n重新赋值
    n = 10
    myWhile(n >= 1)({
      println(n)
      n -=1
    })

简化代码

    //3.用匿名函数简化代码
    def myWhile2(condition: =>Boolean): (=>Unit)=>Unit = {
      //内层实现递归 参数就是循环体-代码块
      op => {
        if (condition){
          op
          myWhile2(condition)(op)
        }
      }
    }

    //4.用柯里化简化代码
    def myWhile3(condition: => Boolean)(op: =>Unit): Unit = {
      if (condition){
        op
        myWhile2(condition)(op)
      }
    }

惰性加载

        懒加载(Lazy Loading)是一种常见的优化策略,它可以延迟对象的初始化时间,减少应用程序的启动时间和内存消耗。在懒加载的策略下,只有在需要访问具体的对象时才会进行初始化。在Scala中,我们使用关键字 'lazy' 来实现懒加载。

lazy val res: Int = compute()

def compute(): Int = {
  // 进行复杂的计算
  Thread.sleep(1000)
  //返回计算结果
  100
}

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

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

相关文章

【LeetCode】 动态规划 刷题训练(三)

文章目录 931. 下降路径最小和题目解析状态转移方程完整代码 64. 最小路径和题目解析状态转移方程完整代码 174. 地下城游戏题目解析状态转移方程完整代码 931. 下降路径最小和 点击查看&#xff1a;下降路径最小和 给你一个 n x n 的 方形 整数数组 matrix &#xff0c;请你找…

chatgpt赋能python:Python搜题软件:为你量身定制的智能搜索助手

Python搜题软件&#xff1a;为你量身定制的智能搜索助手 随着互联网的普及&#xff0c;我们每天需要面对海量的信息&#xff0c;其中包含了我们所需要的一切答案。但是&#xff0c;如何快速而准确地找到这些答案呢&#xff1f;这时候&#xff0c;一款智能化的搜题软件就非常有…

【不推荐】win 安装 rust 1.70 (MSVC)

目录 一、下载二、安装三、配置环境变量四、检查是否安装成功五、参考文章 一、下载 官网地址&#xff1a;https://www.rust-lang.org/zh-CN/ https://forge.rust-lang.org/infra/other-installation-methods.html 历史版本下载地址&#xff1a; 二、安装 注意&#xff1a;安…

Redis:redis基于各大实战场景下的基本使用

文章目录 前言String 命令实战1.业务缓存对应redis中的指令伪代码 2.分布式锁对应redis中的指令伪代码 3.限流对应redis中的指令伪代码 List 命令实战1.提醒功能对应Redis中的指令伪代码 2.热点列表对应Redis中的指令伪代码 Hash 命令实战1.用户资料缓存对应redis中的指令伪代码…

RabbitMQ学习笔记(尚硅谷)

文章目录 一. 消息队列1. 定义2. 作用2.1 流量消峰2.2 应用解耦2.3 异步处理 3. 分类4. MQ的选择5. RabbitMQ5.1 概念5.2 四大概念5.3 六大模式5.4 RabbitMQ 工作原理5.5 安装 6. 代码实现 二. Hello World (简单模式)1. 生产者代码2. 消费者代码 三. Work Queues (工作队列模式…

OUC编译原理实验报告 实验5:用Yacc设计语法分析器1 实验6:用Yacc设计语法分析器2

编译原理实验报告 实验5&#xff1a;用Yacc设计语法分析器1 实验6&#xff1a;用Yacc设计语法分析器2 中国海洋大学编译原理实验2023春 仅供同学参考思路 请勿直接抄袭 否则可能喜提0分 目录 文章目录 编译原理实验报告目录一.实验目的二.实验内容实验5实验6 三.实验要求实…

RISCV Reader笔记_2 RV32I

RV32I 完整的RV32I指令集可以用下面的式子中出现单词的首字母表示&#xff1a; 比如这一条&#xff1a; set less than {immediate} {unsigned} 也就是slt slti sltu sltiu这4个指令。 RISCV指令格式如下。R 寄存器操作&#xff0c;I 立即数或load访存&#xff0c;S store访…

C51单片机期末复习第八章单片机接口技术

一 总线&#xff1a; 传送同类信息的连线 三总线&#xff1a; 地址总线AB&#xff0c;数据总线DB,控制总线CB 目录(ppt给的没啥用&#xff0c;乱还不全)&#xff1a; 8.1 单片机的系统总线 8.2 简单并行I/O口扩展 8.3 可编程并行I/O口扩展 8.4 D/A转换与DAC0832应用 8…

稀疏表:最大公约数

问题描述 给定一个数组, 每次操作可以选择数组中任意两个相邻的元素 x , y x, y x,y 并将其 中的一个元素替换为 gcd ⁡ ( x , y ) \operatorname{gcd}(x, y) gcd(x,y), 其中 gcd ⁡ ( x , y ) \operatorname{gcd}(x, y) gcd(x,y) 表示 x x x 和 y y y 的最大公约数。 请…

MIT 6.S081 教材第五章内容 -- 中断与设备驱动--下

MIT 6.S081 教材第五章内容 -- 中断与设备驱动--下 引言关于RISC-V特权级架构说明RISC-V特权模式OpenSBI介绍RISC-V启动过程RISC-V中的异常M模式下的异常1. 硬件中断的处理&#xff08;以时钟中断为例&#xff09;2. M模式下的异常相关寄存器3. 同步异常的处理 S模式下的异常1.…

chatgpt赋能python:Python散点图介绍:如何用Python绘制散点图?

Python散点图介绍&#xff1a;如何用Python绘制散点图&#xff1f; Python是一门流行的编程语言&#xff0c;用于解决各种问题和编写各种应用程序。其中&#xff0c;数据可视化是Python应用程序中非常重要的组成部分。散点图是最常用的数据可视化图形之一&#xff0c;它能够清…

拓扑排序:神经网络

题目链接 神经网络 题目大意 在兰兰的模型中&#xff0c;神经网络就是一张有向图&#xff0c;图中的节点称为神经元&#xff0c;而且两个神经 元之间至多有一条边相连&#xff0c;下图是一个神经元的例子&#xff1a; 图中&#xff0c; X 1 — X 3 X_1—X_3 X1​—X3​是信…

从类加载到双亲委派:深入解析类加载机制与 ClassLoader

目录 前言Class 文件介绍如何生成 class 文件观察 Bytecode 方法class 文件到底是什么样的呢&#xff1f; Class 加载、链接、初始化加载、类加载器双亲委派Launcher 核心类ClassLoader 相关源码ClassLoader 相关问题自定义简单 ClassLoader自定义加密 ClassLoader打破双亲委派…

动态ip与静态ip的概念、区别、应用场景

动态ip与静态ip的区别 前言一、介绍IP地址的概念和作用1.1、IP地址的定义1.2、IP地址的作用 二、动态IP和静态IP的区别2.1、动态IP和静态IP的定义2.2、动态IP和静态IP的特点2.3、动态IP和静态IP的优缺点比较 三、动态IP和静态IP的应用场景3.1. 动态IP的应用场景3.2. 静态IP的应…

利用numpy解决解方程组的基本问题

1 问题 进入大学&#xff0c;我们接触了线性代数&#xff0c;利用线性代数解方程组比高中慢慢计算会好了许多&#xff0c;快捷许多&#xff0c;我们作为编程人员&#xff0c;有没有用python解决解方程组的办法呢&#xff1f; 2 方法 我们提出使用python的numpy解方程。 找到用于…

11- C程序的组成结构 (C语言)

一、C程序的基本组成结构 1、源文件: 后缀为.c 的文件2、头文件&#xff1a;后缀为.h的文件 注意&#xff1a; 源文件 功能&#xff1a;实现程序功能头文件 功能&#xff1a;函数的声明、全局变量的声明、宏定义、类型的声明一个由C语言所组成的项目中 只允许有一个main函数 …

离散数学大作业任务书

目 录 实际的练习题目、系统的总功能和各子模块的功能………………………………………………………………………………1 1.1题目及问题描述………………………………………………………………1 1.2功能概述………………………………………………………………………1 1.3技…

02 | 日志系统:一条SQL更新语句是如何执行的?

以下内容出自《MySQL 实战 45 讲》 02 | 日志系统&#xff1a;一条SQL更新语句是如何执行的&#xff1f; 查询语句的那套流程&#xff0c;更新语句也会走一遍。 更新流程中和查询不一样的是&#xff0c;更新流程中涉及了两个重要的日志模块。redo log (重做日志) 和 binglog&a…

如何编写用于Neo-Hookean材料的Abaqus VUMAT Fortran子例程

引言 大家好&#xff0c;我是一个热爱编程、研究有限元分析的普通程序员。我非常感谢你们能够抽出宝贵的时间来阅读我的文章&#xff0c;你们的支持是我前行的动力。今天&#xff0c;我们将讨论一个非常专业的话题&#xff0c;即如何编写用于Neo-Hookean材料的Abaqus VUMAT Fo…

Unreal 5 实现UI制作

这一篇讲解一下unreal engine里面的内置ui插件UMG&#xff0c;虚幻示意图形界面设计器&#xff08;Unreal Motion Graphics UI Designer&#xff09;(UMG) 是虚幻引擎内置的一套ui制作工具&#xff0c;通过它我们能够实现平面ui&#xff0c;场景hud内容 实现背景图片填充整个…