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

logo.81433277.png

基本概念

一、变量与标识符

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中添加字面量后缀(适用于整数和浮点数类型)。

整数类型Int8Int16Int32Int64UInt8UInt16UInt32UInt64
字面量后缀i8i16i32i64u8u16u32u64
浮点数类型Float16Float32Float64
字面量后缀f16f32f64

​ 看的有点蒙了?下面来看一些声明示例吧~

// 声明整型变量
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 : stepstart..=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 类型。

发布于:2024年12月13日 22:27