从0开始学习仓颉语言(第一章)——变量与标识符

基本概念
一、变量与标识符
1.1变量名与基本变量类型(含示例)
变量名可以由英文字母或者下划线开头,后面可以跟多个英文字母、数字或者下划线。例如:fenghua_123、_yeah_123。在关键字的外面加上一对反引号,用于将关键字作为变量。
变量是将一个名字和一个特定类型的值关联起来。变量分为:可变变量、不可变变量和常量。其中可变变量的值可以在运行时改变,不可变变量和常量的值不可以改变,他们的区别在于:常量为编译时求值,而不可变变量的值是在运行时求得的。下方为变量声明的形式:【注:当初始值具有明确的类型时,可以省略变量类型标注,编译器会自动推断出变量类型】
var name: type = expr
let name: type = expr
const name: type = expr_const
在仓颉中,拥有整数(Int*,UInt*)、浮点数(Float*)、字符与字符串(Rune,String)、布尔(Bool)类型。同时声明变量类型也可以分为两种方式,一种是在上方的声明type中指定,另一种是在expr中添加字面量后缀(适用于整数和浮点数类型)。
| 整数类型 | Int8 | Int16 | Int32 | Int64 | UInt8 | UInt16 | UInt32 | UInt64 |
|---|---|---|---|---|---|---|---|---|
| 字面量后缀 | i8 | i16 | i32 | i64 | u8 | u16 | u32 | u64 |
| 浮点数类型 | Float16 | Float32 | Float64 |
|---|---|---|---|
| 字面量后缀 | f16 | f32 | f64 |
看的有点蒙了?下面来看一些声明示例吧~
// 声明整型变量
let a: Int64 = 2024
let b = 67u8 // 使用字面量后缀声明 UInt8 变量
// 声明浮点型变量
let c: Float64 = 6.21
// 声明布尔型变量
let d: Bool = true || false
// 声明字符型变量
let e: Rune = '曼'
let f: Rune = '\u{9889}' // 支持Unicode编码
let g: String = "mamba" + "out" // 支持字符拼接
let h: String = """
Haha, what can I say?
Mamba out!
""" // 支持长文本
let i: String = "Year${a}" // 支持插值字符串
下面为一个求近似圆周率的示例,用于研究变量的声明与使用【注:博主在这里踩坑了,如果根据b站的入门视频来看,这里应该是from std import random.*,然后报错expected declaration, found 'from', only declarations or macro expressions can be used in the top-level,改为import std.random.*后解决(具体原因还没有研究明白)】:
import std.random.*
import std.math.*
main() {
// 这里会自动推断类型
const N = 100000u32
// 这里为显式指定
var n: UInt32=0
let random=Random()
for (_ in 0..N){
let x=random.nextFloat64()
let y=random.nextFloat64()
if ((x-0.5)**2+(y-0.5)**2<0.25){
n++
}
}
let pi=Float64(n)/Float64(N)*4.0
println("pi ≈ ${pi}")
println("deviation: ≈ ${abs(Float64.PI - pi)}")
}
1.2其他数据类型
1.2.1数组
我们可以使用 Array 类型来构造单一元素类型,有序序列的数据。我们可以使用 Array<T> 来表示 Array 类型。T 表示 Array 的元素类型,T 可以是任意类型。以下我用代码来演示Array的一些操作:
// 普通的声明方式
let a: Array<String> = [] // 创建一个空的数组
let b = [1, 2, 3, 3, 2, 1]
// 构造声明方式
let a = Array<Int64>() // 创建一个空的数组
let b = Array<Int64>(a) // 复制a的元素并创建数组b
let c = Array<Int64>(3, item: 0) // 创建一个长度为3的,元素值都为0的数组
let d = Array<Int64>(3, {i => i + 1}) // 使用lamba表达式初始化元素
// 获取数组的长度
let a = [1, 2, 3, 3, 2, 1]
println("Array a=${a}, size is ${a.size}")
// 遍历数组
// 1. 直接遍历数组元素
for (i in a) {
println("The element is ${i}")
}
// 2. 使用下标遍历数组元素
for (index in 0..a.size){
println("The ${index}th element is: ${a[index]}")
}
let b = a[1..3] // 截取a数组中第2到4个元素,这里的b为引用,数据与a同步
b[1]=10 // 修改b的同时,a同步修改
a[2]=160 // 修改a的同时,b同步修改
println("After: a=${a} b=${b}") // After: a=[1, 2, 160, 3, 2, 1] b=[2, 160]
1.2.2元组
元组可以将多个不同的类型组合在一起,成为一个新的类型,一旦定义了一个元组的实例类型,他就长度固定且内容不可变。以下为一些使用方式:
var pi = (3.14, "PI")
println(pi[0]) // 元组中的元素可以通过下标的形式来访问
println(pi[1])
// 在赋值表达式中,可使用元组字面量对表达式的右值进行解构
var a: Int64
var b: String
var c: Unit
func f() { ((1, "abc"), ()) }
// 元组可以标记显式的类型参数名,下面的name和price参数名可以全部不指定,也可以全部都指定(不能只写一部分)
func getFruitPrice (): (name: String, price: Int64) {
return ("banana", 10)
}
((a, b), c) = f() // a为1,b为"abc",c为()
((a, b), _) = ((2, "def"), 3.0) // a为2, b为"def",使用_可以忽略掉元组中指定位置的元素
1.2.3区间类型
区间类型用于表示拥有固定步长的序列,区间类型是一个泛型,使用 Range<T> 表示(T类型必须支持关系操作符,并和Int64类型值做加法)。区间有左闭右开和左闭右闭两种形式,他们的格式分别为start..end : step和start..=end : step,通俗理解就是,‘=’可以包含end。需要补充的一点是:当区间不合法时,表达式生成的是空区间。下面为一段代码示例:
let n = 10
let r1 = 0..10 : 1 // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
let r2 = 0..=n : 1 // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
let r3 = n..0 : -2 // 10, 8, 6, 4, 2
let r4 = 10..=0 : -2 // 10, 8, 6, 4, 2, 0
let r5 = 0..10 : -1 // 空区间
let r6 = 10..0 : 1 // 空区间
// 当然,也可以使用构造的方式进行实例化
// Range<T>(start: T, end: T, step: Int64, hasStart: Bool, hasEnd: Bool, isClosed: Bool)
let r7 = Range<Int64>(0, 10, 1, true, true, true) // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
let r8 = Range<Int64>(0, 10, 1, true, true, false) // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
let r9 = Range<Int64>(10, 0, -2, true, true, false) // 10, 8, 6, 4, 2
1.2.4Nothing类型
Nothing 是一种特殊的类型,它不包含任何值,并且 Nothing 类型是所有类型的子类型。目前编译器还不允许在使用类型的地方显式地使用 Nothing 类型。
