首页 > 其他分享 >[读书日志]从零开始学习Chisel 第二篇:Scala的变量与函数(敏捷硬件开发语言Chisel与数字系统设计)

[读书日志]从零开始学习Chisel 第二篇:Scala的变量与函数(敏捷硬件开发语言Chisel与数字系统设计)

时间:2025-01-03 21:30:28浏览次数:3  
标签:函数 val scala Int Scala Chisel 从零开始 参数 变量

第一篇icon-default.png?t=O83Ahttps://blog.csdn.net/m0_74021449/article/details/144887921

2.2 Scala的变量及函数

2.2.1变量定义与基本类型

变量声明

变量首次定义必须使用关键字var或者val,二者的区别是val修饰的变量禁止被重新赋值,它是一个只读的变量。首次定义变量时必须赋值进行初始化。var类型的变量重新赋值时新旧必须是同一个类型,而val类型变量无法被重新赋值。变量定义具有覆盖性,后声明的变量会覆盖前面的变量。

Scala推荐使用val定义变量,函数式编程的思想之一就是传入函数的参数不应该改变。需要说明的是,使用val定义的变量并不是不可变,而是这个指向关系不可变。当这个变量被声明,变量指向的对象就是唯一确定的,但这个对象本身可以改变,只是变量指向的位置不可变。

基本类型

Scala作为一种静态语言,在编译时会检查每个对象的类型,类型不匹配的非法操作会报错。Scala定义了一些标准类型:

类型说明
Byte8bit有符号整数,补码表示,范围是-27~27-1
Short16bit有符号整数,补码表示,范围是-215~215-1
Int32bit有符号整数,补码表示,范围是-231~231-1
Long64bit有符号整数,补码表示,范围是-263~263-1
Char16bit无符号字符,Unicode编码表示,范围是0~216-1
String字符串类型,属于java.bang包
Float32bit单精度浮点数
Double64bit双精度浮点数
Boolean布尔值,true or false

定义变量时可以指定类型,也可以让编译器自动推断。显式指定类型的示例:

scala> val x: Int = 123
val x: Int = 123

scala> val y: Long = 123
val y: Long = 123

整数字面量

如果单独出现数字,没有任何说明,默认推断为Int类型;结尾有l或L的推断为Long类型,以0x开头的认为是十六进制,不区分大小写。Byte和Short类型需要显示指定:

scala> val x = 100
val x: Int = 100
​
scala> val y = 100L
val y: Long = 100
​
scala> val z: Byte = 200
-- [E007] Type Mismatch Error: -----------------------------------------------------------------------------------------
1 |val z: Byte= 200
  |             ^^^
  |             Found:    (200 : Int)
  |             Required: Byte
  |
  | longer expla

Byte类型最大不超过127,超过限制的赋值将报错

浮点数字面量

浮点数字面量都是十进制的,默认是Double类型,En或en表示科学计数法10的n次方,末尾加一个f或F表示Float,D或d表示Double。Double字面量不能赋值给Float类型变量。

scala> val x: Float = -3.2
val x: Float = -3.2
​
scala> val x = -3.2
val x: Double = -3.2
​
scala> val x: Float = 3.2D
-- [E007] Type Mismatch Error: -----------------------------------------------------------------------------------------
1 |val x: Float = 3.2D
  |               ^^^^
  |               Found:    (3.2d : Double)
  |               Required: Float
  |
  | longer explanation available when compiling with `-explain`
1 error found

字符字面量和字符串字面量

以单引号括住的一个字符表示一个字符字面量,采用Unicode编码,也可以使用'\u编号'来构建一个字符,本书说明Unicode编码可以出现在名称命名处,但在最新版本Scala编译器中会报错:

scala> val a = 'a'
val a: Char = a
​
scala> val \u0041 = 'a'
-- [E032] Syntax Error: ------------------------------------------------------------------------------------------------
1 |val \u0041 = 'a'
  |           ^
  |           pattern expected
  |
  | longer explanation available when compiling with `-explain`

字符串字面量是用双引号“”括住的字符序列,长度是任意的。字符和字符串都支持转义字符。

字符串插值

表达式可以被嵌入在字符串字面量中被求值,有三种实现方法,一种是s插值器,一种是raw插值器,一种是f插值器。

s插值器形如 s"……${表达式}……"其中花括号可以不加,但只会识别美元符号到首个非标识字符(字母、数字、下划线和操作符的组合交标识符,以及字符串)对于非标识字符如果想要求值,必须使用花括号:

scala> val name = "Jia"
val name: String = Jia

scala> s"Name = $name"
val res3: String = Name = Jia

scala> s"Result = ${1+2}"
val res4: String = Result = 3

raw插值器和s插值器类似,区别是不识别转义字符。

f插值器使用方法更灵活,支持格式控制:

scala> printf(f"${math.Pi}%.5f")
3.14159~

2.2.2函数及其几种形式

函数的定义

不多说,直接看代码:

scala> def max(x : Int, y: Int): Int = {
     | if(x > y)
     | x
     | else
     | y
     | }
def max(x: Int, y: Int): Int

说明:x,y是输入参数(形参)后面跟的是它们的类型;Int={}是函数体,表示返回是一个Int类型整数。

备注:左侧的|只是编译器自己生成的,用于多行的代码,并不是语法体系中的一部分。

  1. 分号推断:语句末尾的分号是可选的,编译器会自动推断分号。如果一行有多条语句,则必须用分号隔开。

  2. 函数的返回结果:return关键字是可选的,编译器自动为函数体中的最后一个表达式加上return,建议不要显示声明return。返回结果的类型也可以自动推断,也就是说上面的Int = {},Int也是可以省略的。Uint类型表示无返回值,它不是必须的,编译器也可以自动推断;但如果显式声明Uint,则即便有可以返回的值也不会返回任何值。

  3. 等号与函数体:函数体是花括号括起来的部分,里面有多条语句,可以自动推断分号,返回最后一个表达式。当函数的返回类型没有显示声明,等号可以省略,但此时返回类型会变成Uint,建议不要省略等号,并且用显示声明返回类型。

  4. 无参函数:无参函数可以写空括号作为参数列表,也可以不写;但如果没有空括号,调用的时候禁止写空括号。

方法

方法指定义在class,object,trait中的函数,成为成员函数或方法,这与其他面向对象特性的语言一致。

嵌套函数

函数体内部可以嵌套定义内部函数,但无法被外界访问。

函数字面量

函数式编程认为函数的地位和一个Int值,String值是一样的,因此函数也可以成为一个函数的参数或返回值,也可以把函数赋值给一个变量。函数字面量是一种匿名函数,它可以存储在变量中,成为函数参数,或者当作返回值返回,定义形式是:

{参数1:参数1类型,参数2:参数2类型...} => {函数体}

它可以更精简地使用,用下划线作为占位符替代参数,只保留函数体:

scala> val f = (_: Int) + (_: Int)
val f: (Int, Int) => Int = Lambda/0x00000003015d55a8@6816b0cb

scala> f(1,2)
val res5: Int = 3

用def定义的函数和函数字面量都可以以函数为参数,返回函数。

scala> val add = (x:Int) => {(y:Int) => x + y}
val add: Int => Int => Int = Lambda/0x00000003015d6348@20f1e26

scala> add(1)(10)
val res6: Int = 11

这里是函数的嵌套,第一个函数表示输入是x,类型是Int,函数体是(y:Int)=> x+y这是一个函数字面量,它的输入是y,类型是Int,函数体是x+y,并且把这个表达式结果返回。如果调用add(1),则返回的是(y:Int)=>1+y这个函数字面量。

scala> def aFunc(f:Int => Int) = f(1) + 1
def aFunc(f: Int => Int): Int
​
scala> aFunc(x=>x+1)
val res7: Int = 3

aFunc的参数f是一个函数,它的输入类型是Int,返回类型也是Int,调用时传入的参数是函数字面量x=>x+1,首先计算f(1)=2,再计算返回值是3。

部分应用函数

上面的函数字面量实现了函数作为一等值的功能,使用def定义的函数也具有相同的功能,只不过需要借助部分应用函数的形式来实现。部分应用函数的意思就是给出函数的一部分参数,使其可以赋值给变量或者当作函数参数进行传递。废话不多数,直接上代码:

scala> def sum(x:Int,y:Int,z:Int) : Int = {x + y + z}
def sum(x: Int, y: Int, z: Int): Int
​
scala> val a1 = sum(4,5,6)
val a1: Int = 15
​
scala> val a2 = sum(4,_:Int,6)
val a2: Int => Int = Lambda/0x000000030167d3a0@c3acda3
​
scala> a2(5)
val res12: Int = 15
​
scala> val a3 = sum _
1 warning found
-- Warning: ------------------------------------------------------------------------------------------------------------
1 |val a3 = sum _
  |         ^^^^^
  |         The syntax `<function> _` is no longer supported;
  |         you can simply leave out the trailing ` _`
val a3: (Int, Int, Int) => Int = Lambda/0x00000003016767e0@2c427287
​
scala> val a3 = sum
val a3: (Int, Int, Int) => Int = Lambda/0x0000000301676e10@68603829
​
scala> a3(4,5,6)
val res9: Int = 15

注意:书中的写法 sum _在最新版的编译器中不被支持,现在无需写下划线,只需要把函数名直接赋值给变量即可直接调用。

scala> def needSum(f:(Int,Int,Int) => Int) = f(1,2,3)
def needSum(f: (Int, Int, Int) => Int): Int
​
scala> needSum(sum)
val res13: Int = 6

这里我们已经逐渐能够感受到函数式编程的魅力,当理解这种调用方式后将会有很灵活的应用。

闭包

一个函数除了使用它的参数以外,还可以使用定义在函数以外的其他变量,其中函数的参数成为绑定变量,函数以外的变量称为自由变量,这样的函数称为闭包。函数捕获的自由变量是函数定义之前的自由变量,若后面出现新的同名自由变量将前面的自由变量将其覆盖,函数与其无关。但如果自由变量是用var创建的可变对象,那么闭包随之改变。

scala> var a1 = 1
var a1: Int = 1
​
scala> val a2 = 100
val a2: Int = 100
​
scala> val add1 = (x : Int) => x + a1
val add1: Int => Int = Lambda/0x000000030167fb08@23b456ac
​
scala> val add2 = (x : Int) => x + a2
val add2: Int => Int = Lambda/0x00000003016843f0@525b416f
​
scala> add1(1)
val res14: Int = 2
​
scala> add2(1)
val res15: Int = 101
​
scala> a1 = 100
a1: Int = 100
​
scala> add1(1)
val res16: Int = 101
​
scala> var a1 = 1
var a1: Int = 1
​
scala> add1(1)
val res17: Int = 101
​
scala> val a2 = 1
val a2: Int = 1
​
scala> add2(1)
val res18: Int = 101

阅读这段程序的对比已经很明显地了解到它的性质。在后面改变var变量的值,闭包随之改变,但对于var和val变量重新定义将前面的覆盖,闭包的值不会随之改变。

函数的特殊调用形式

  1. 具名参数:函数调用时传入的参数是按照先后顺序传递的,但如果显式声明参数的名字,可以无视参数顺序。按位置传递的参数和按名字传递的参数可以混用。

  2. 默认参数值:函数定义时可以给参数一个默认值,如果调用函数缺省了这个参数,会使用默认值。

  3. 重复参数:允许把函数的最后一个参数标记为重复参数,形式在最后一个参数的类型后面加上星号。

柯里化

对于大多数编程语言而言,函数只能有一个参数列表,但是列表中可以有若干用逗号间隔的参数。Scala的特性柯里化允许一个函数可以有任意个参数列表,它与另一个语法搭配使用,即当参数列表中只有一个参数时,调用该函数时允许单个参数不用圆括号括起来,改用花括号也可以。

scala> def add(x:Int)(y:Int)(z:Int) = x+y+z
def add(x: Int)(y: Int)(z: Int): Int

scala> add(1)
val res19: Int => Int => Int = Lambda/0x0000000301686b40@7ece1800

scala> add(1)(2)
val res20: Int => Int = Lambda/0x00000003016872f0@3b3e9814

scala> add(1)(2)(3)
val res21: Int = 6

scala> add(1)(2){3}
val res22: Int = 6

scala> add{1}(2)(3)
val res23: Int = 6

在这里我们看出来其实柯里化就是把它处理成了一个嵌套的函数。

传名参数

如果一个函数是无参函数,调用函数时,传递进去的函数字面量可以只写函数体。

//常规调用方法
scala> val a1 = 1
val a1: Int = 1
​
scala> val a2 = 2
val a2: Int = 2
​
scala> def add(f: () => Int) = {a1 + a2 + f()}
def add(f: () => Int): Int
​
scala> add(() => 1)
val res24: Int = 4
​
//传名参数调用方法
scala> def add2(f: => Int) = {a1 + a2 + f}
def add2(f: => Int): Int
​
scala> add2(1)
val res26: Int = 4

注:这里传入的f函数是一个没有输入,直接返回传入的值的函数。

这两种版本都是在使用到传入的函数时才会计算这个函数中的表达式,但如果改写成下面这样:(去掉=>),那么就变成先计算表达式再传入值。

scala> def add3(f:Boolean) = {
     | if(f)
     | a1 + a2
     | else
     | 0
     | }
def add3(f: Boolean): Int
​
scala> add3(1<2)
val res27: Int = 3
​
scala> add3(1>2)
val res28: Int = 0
​
scala> add3(1/0)
-- [E007] Type Mismatch Error: -----------------------------------------------------------------------------------------
1 |add3(1/0)
  |     ^^^
  |     Found:    Int
  |     Required: Boolean
  |
  | longer explanation available when compiling with `-explain`
1 error found

标签:函数,val,scala,Int,Scala,Chisel,从零开始,参数,变量
From: https://blog.csdn.net/m0_74021449/article/details/144918271

相关文章

  • 如何构建一个现代化的餐饮管理智能大屏“ “Vue.js 和 ECharts 结合:打造餐饮数据可视
    效果图:完整代码<!DOCTYPEhtml><html><head><metacharset="utf-8"><title>餐饮管理智能大屏</title><scriptsrc="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script><s......
  • 如何构建一个现代化的AI数据洞察平台“ “Vue.js 和 ECharts 结合:打造数据可视化大屏
    效果图:完整代码<!DOCTYPEhtml><html><head><metacharset="utf-8"><title>AI数据洞察平台</title><scriptsrc="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script><sc......
  • scala图书管理系统【ui】软件包
    LibrarayPresentationpackageorg.apppackageuiimportorg.app.modeis.{BookModel,UserModel}importorg.app.service.{BookService,UserService}importscala.io.StdInimportscala.io.StdIn.readLineclassLibrarayPresentation{ privatevalBookService=new......
  • Scala语言的函数实现
    Scala语言中的函数实现:优雅与高效并存的艺术在编程的世界里,函数是构建软件的基本单元之一,它不仅体现了代码的逻辑和结构,更是程序设计思想的具体体现。Scala,作为一种融合了面向对象和函数式编程特性的现代编程语言,其对函数的支持尤为丰富和强大。本文将深入探讨Scala语言中函数......
  • 从零开始:Python 新增的注解功能(Type Hints)
    适用读者:对Python有一定基础,想了解Python注解(TypeHints)以及它在代码可读性、调试与维护方面的作用的朋友们。一、什么是Python注解(TypeHints)?简单来说,**Python注解(TypeHints)**就是在变量或函数上标记“希望(或建议)它是某种类型”,从而帮助我们和其他开发者更好地理......
  • 从零开始学习黑客技术,看这一篇就够了
    ......
  • 从零开始学习黑客技术,看这一篇就够了
    ......
  • 游戏开发-从零开始 003
    搁浅了很多年,重新拾遗。之前陆续发了3款小游戏,很久没维护之后ios上面都下架了。每年688,不说了。都是泪。进入正题。23年AI大火,逼迫很多人下岗,但好的方面是解放了生产力生成式AI的崛起让独立游戏找到了新方向,AI绘画,AI音乐,AI编程,甚至不需要懂编程,就可以创造出一款游戏。游戏......
  • 从零开始学TiDB(8) TiFlash 主要架构
    一.TiFlash的主要架构 二.TiFlash主要功能1.异步复制2.一致性读取 T0时刻从客户端写入两行数据k=1value=100k=999value=7分别写入到了两个region,并且产生raftlog 此时TiFlash还没有TiKV的这两行数据此时TiFlash同步了key=1value=100的数据还没有同步ke......
  • 从零开始构建React Native数字键盘功能
    从零开始构建ReactNative数字键盘功能发布于 2024-03-0113:58:333230举报文章被收录于专栏:终身学习者现代移动应用程序在入门过程中经常涉及一个步骤,你需要输入发送到你的电子邮件或手机号码的验证码PIN。有时,你需要使用类似于分割OTP输入字段的东......