Skip to content

Latest commit

 

History

History
828 lines (605 loc) · 31.1 KB

vbook_05.md

File metadata and controls

828 lines (605 loc) · 31.1 KB

第5章 V中的数组和映射

现在我们已经了解了V中的基本概念,包括变量、常量和原始数据类型,在本章中,我们将专注于数组和映射。我们将学习声明数组的不同方法以及在其声明期间使用各种属性来初始化它们。

此外,我们将探讨在数组中使用in<<运算符的方法。然后,我们将通过固定大小数组和多维数组的处理过程来更好地理解数组。

我们还将实现在数组上执行的最常见操作,包括克隆、排序和过滤技术,来更好地理解数组。

在本章中,我们还将学习映射,它以键值对的形式保存数据。 V中的映射通常被称为字典,就像其他编程语言(如C#和Python)一样。我们将探讨如何处理映射并了解声明和初始化映射的各种方法。

我们还将学习如何在映射中执行各种操作,例如检索给定键的键值对,以及使用or块处理检索不存在的键。然后,我们将学习如何向映射中添加、更新和删除键值对。

本章中涵盖的主题包括:

  • 数组
  • 映射

技术参考

本章完整的源代码可在 https://github.com/ToughStyle/V-programming-book-cn/tree/main/codes/Chapter05 中获得。

数组

数组用于表示项目的集合。这些项目可以是相似的数据类型,例如学生获得的成绩列表或家庭成员的姓名。数组的项必须属于相似的数据类型。

在V中,数组可以容纳由基本类型或高级类型(如结构体)组成的相似数据类型的元素。默认情况下,当创建数组时,内存分配发生在堆上。您可以创建一个在堆栈内分配内存的固定大小的数组。数组可以有多个维度。

默认情况下,V中的数组是不可变的。可以使用mut关键字声明可变的数组。

以下是声明数组的语法:

arr := [VAL_1, VAL_2, .. VAL_N]

上述语法演示了一维不可变数组的声明。变量名为arr,后跟:=符号。该语句右侧有一系列的值,这些值必须是相似的数据类型。VAL_1VAL_2等的值必须使用逗号分隔,并且所有这些值都应该用方括号[]括起来。

声明数组的不同方法

数组可以以多种方式声明。我们将进一步详细说明每种方法。请注意,默认情况下数组是不可变的,如果我们想要修改元素或添加元素到数组中,必须使用mut关键字声明它们为可变的。

声明并初始化数组

可以同时声明和初始化数组中的多个值。以下是大多数编程语言允许您声明和初始化数组的一般方式:

mut sports := [ 'cricket', 'hockey', 'football' ]

在这里,我们声明了一个可变数组,命名为sports,并用字符串类型的元素初始化了数组。sports数组的长度为3,也就是这个数组中元素的数量。

声明空数组

必须使用mut关键字声明空数组,这样稍后才能添加或更新元素。以下代码显示了如何声明空数组:

mut <VAR_NAME> := []DATA_TYPE{}

上述语法显示了空数组的声明。它以mut关键字开始,后跟VAR_NAMEVAR_NAME可以是任何有意义的名称。然后,我们定义一个空的[]方括号,没有在:=运算符的右侧指定数组的大小。 DATA_TYPE可以是任何原始类型或高级类型,例如结构体。然后,跟着未定义任何属性的{}大括号。

让我们根据上述语法创建一个空字符串数组:

mut animals := []string{}
println(animals) // 输出空数组: []

现在,我们将在可变的animals字符串数组上添加一个值:

animals << 'Chimpanzee'
animals << 'Dog'
println(animals) // ['Chimpanzee', 'Dog']

使用数组属性声明数组

V允许您通过指定数组的属性值,例如capleninit来声明数组。

使用len属性声明数组

在声明数组时,可以将len数组属性分配给一个值,如下所示:

mut i := []int{len: 3}
println(i)

输出结果如下:

[0, 0, 0]

在上面的代码中,我们声明了一个具有默认长度3的可变整数数组。当我们打印数组时,我们注意到i数组具有三个元素,在数组的三个位置中分配了一个默认的整数值0。这种声明数组的方法需要重新分配数组元素的值。

使用init属性声明具有默认初始值的数组

我们可以在声明数组时使用init属性为所有元素分配默认值,如下所示:

mut j := []int{len: 3, init: 1}
println(j)

输出结果如下:

[1, 1, 1]

上述代码演示了init属性的使用,其中我们声明了一个具有初始长度为3的整数数组,并使用指定的默认值1填充了所有三个位置。

使用cap属性声明具有初始容量的数组

还可以在声明期间指定数组的容量来定义数组。这种声明可变数组的方法通常可以提高向数组插入元素的性能:

mut k := []int{cap: 2}
println(k)

输出结果如下:

[]

在这里,我们声明了一个具有初始容量2的可变整数数组,并将其设置为cap数组属性。请注意,我们输出k数组时的结果是一个空数组。因此,很明显,使用设置了cap属性的声明数组减少了需要重新分配的次数。

我们可以使用<<运算符向数组添加值,例如k<<1。我们将在本章后面的部分学习更多关于使用<<运算符向数组添加值的内容。

使用lencap数组属性

一旦声明了数组,该数组将公开两个只读属性,即lencaplen属性表示数组的长度。具体来说,长度表示数组中实际存在的元素的数量。另一个属性cap表示数组的容量。 让我们考虑以下展示数组属性使用的代码:

mut sports := ['cricket', 'hockey', 'football']
println(sports.len) // sports数组的长度
println(sports.cap) // sports数组的容量

以下是输出结果:

3
3

我们可以观察到,字符串类型的sports数组的长度和容量都为3。由于sports数组是可变的,让我们看看在对该数组执行各种操作期间,长度和容量如何改变。

例如,如果我们从sports数组中删除一个元素,则长度减少一个,而容量保持不变:

sports.delete(2) // 删除football
println('Length of sports array: $sports.len')
println('Capacity of sports array: $sports.cap')

以下是输出结果:

Length of sports array: 2
Capacity of sports array: 3

在这里,我们使用delete函数从索引2处删除了一个元素。delete函数接受要删除的元素的索引。删除元素后,数组的长度为2,但容量仍然是3。

现在,让我们使用<<运算符向sports数组添加两个新的运动:

sports << ['volleyball', 'baseball']
println(sports)
println('Length of sports array: $sports.len')
println('Capacity of sports array: $sports.cap')

以下是输出结果:

['cricket', 'hockey', 'volleyball', 'baseball']
Length of sports array: 4
Capacity of sports array: 6

通过上述操作,我们向sports数组添加了两个新的运动,如排球和棒球。这会将长度从2增加到4,因为我们向数组添加了2种新的运动类型。在添加这两项运动之前,容量为3;在此操作之后,容量变为6,因为每当元素的长度超过当前容量时,容量都会增加一倍。

使用索引访问数组元素

有时,您需要根据其位置访问数组中的特定元素。V允许您通过它们的索引访问数组元素。数组的索引从零开始,您可以根据其索引访问任何特定元素。sports数组有三个元素的长度,因此最后一个元素的索引计算为数组长度-1;在我们的案例中,它是3 - 1,即2。

例如,如果您想访问sports数组中的第二个元素,则可以按以下方式进行:

s := sports[1]
println(s) // 输出hockey

这里,语法向您展示了如何在sports数组中访问索引位置1的项。

使用切片访问数组元素

假设您要访问一系列元素,给定一个范围,那么您可以采用切分数组元素的方法。切片数组的结果为所提供范围内的部分元素数组。下面是用于对数组元素进行切片的语法:

ARRAY_VAR[STARTING_INDEX .. ENDING_POSITION]

上述语法表示切片数组元素:

ARRAY_VAR是数组的变量名。 STARTING_INDEXENDING_POSITION和位于方括号[]中的..运算符。 STARTING_INDEX是表示我们要开始对数组进行切片的元素的索引的整数。 ENDING_POSITION也是一个整数,表示要将元素切成的实际位置,从而得到一个部分数组。 与大多数编程语言(如Python、C#、Java、C等)类似,V中的索引从0开始。这意味着数组中的第一个元素用索引0来表示,下一个元素用索引1表示,依此类推。我们可以假设索引+1是表示数组中元素位置的实际数字。让我们考虑以下示例:

println(sports) // 输出['cricket', 'hockey', 'football']

println(sports[1..3]) // 输出['hockey', 'football']

在上面的代码中,我们对sports数组进行了切片,以输出从索引1开始到位置3的元素。

注意我在这里使用的术语;[1..3] 范围中的第一个数字是1,表示索引,另一个数字3表示位置。

切片数组时有各种条件:

  • 起始索引必须大于或等于0。

  • 起始索引不能大于数组的最后一个索引,即length-1

  • 如果未提供起始索引,则将其解释为0。例如, sports[..2] 语句类似于 sports[0..2]

  • 结束位置必须大于或等于起始索引。

  • 结束位置不能大于数组的长度。

  • 如果未提供结束位置,则将其解释为数组长度的值。例如,对于具有3个元素的数组, sports[1..] 语句类似于 sports[1..3]

与数组一起使用的运算符

为了与数组交互,V具有各种运算符,例如in<<,允许我们在数组上执行各种操作。让我们更详细地了解这些运算符。

in运算符

in运算符可用于查找数组中元素的存在。in运算符的结果是一个布尔值。因此,我们也可以使用!in来表示如果数组中不存在元素则使用此运算符。

使用in运算符与数组结合使用的语法如下所示:

NEEDLE in HAY_STACK

上述语句表示使用in运算符与数组的语法。这里,左侧NEEDLE操作数表示我们要验证它是否在数组中存在。此外,HAY_STACK表示数组的变量名。我们必须确保NEEDLE操作数的类型与HAY_STACK数组持有的元素的类型相匹配。

让我们考虑以下示例:

odd := [1, 3, 5, 7]

println(3 in odd) // 输出:true

println(8 !in odd) // 输出:true

上面的代码演示了in!in运算符的用法。语句3 in odd的结果为真。

<<运算符

<<运算符可用于将值附加到可变数组的末尾。以下代码演示了向整数值数组附加值的过程:

mut even := [2, 4, 6]

even << 8

println(even) // 输出[2, 4, 6, 8]

<<运算符还允许您将数组附加到可变数组中:

even << [10, 12, 14]

println(even) //输出[2, 4, 6, 8, 10, 12, 14]

值得注意的是,<<运算符在V中是多功能的运算符。它用于以下目的:

  • 将值或值数组附加到数组

  • 对整数执行左移操作

我们已经研究了如何定义数组,但是我们声明的数组可以添加无限数量的元素,因为它们不受大小限制。我们还可以定义具有预定义大小的数组,称为V中固定大小的数组。因此,让我们查看如何在V中使用固定大小的数组。

固定大小数组

V允许您定义固定大小的数组。这意味着一旦声明数组后,其长度就无法更改,因此保持不变。为确保根据索引更新元素值,建议使用mut关键字将固定大小的数组声明为可变的。 与其他数组不同,固定大小数组的内存分配发生在堆栈上。堆栈上的内存分配使您可以将固定大小数组用作缓冲区。

一旦声明了大小为n的固定大小数组,数组的所有元素都将填充与数组元素数据类型相对应的默认值。也就是说,如果固定数组是整数,则元素的默认值为0。类似地,字符串类型固定数组的默认值为空''字符串。

定义固定大小数组

让我们考虑以下代码,演示具有四个元素的整数固定大小数组的定义:

mut fix := [4]int{}

println(fix) // [0, 0, 0, 0]

在这里,我们可以看到整数固定大小数组的所有元素的默认值都填充了0。

更新固定大小数组的元素

一旦定义了固定大小数组,您就可以通过使用=符号引用固定大小数组的索引添加元素:

fix[1] = 33

println(fix) //[0, 33, 0, 0]

由于我们在前面的代码中定义了一个可变整数数组fix,因此我们正在尝试使用=号将索引1处的元素的值更新为33。

对于固定大小数组,我们无法使用<<运算符附加值。因此,fix << 44语句将无法执行并抛出错误,指出

error: invalid operation: shift on type [4]int。

固定大小数组的数据类型

让我们看一看使用以下代码定义的固定数组的数据类型:

println(typeof(fix).name) // [4]int

在定义固定数组时给出的长度嵌入在其类型中,并表示为[4]int,用于名为fix的固定数组。

对固定大小数组进行切片会导致普通数组

在固定数组上执行切片会导致普通数组。让我们像下面这样对fix数组进行切片:

s := fix[1..]

println(s) // [33, 0, 0]

让我们看一下结果切片固定数组的类型,如下所示:

println(typeof(s).name) // 输出:[]int

请注意,切片固定数组的结果是一个普通数组。

多维数组

V允许创建多维数组。让我们创建一个二维数组,它在二维平面上保持点坐标(0,0)(0,1)(1,0)(1,1),表示单位长度的正方形:

mut coordinates_2d := [][]int{len: 4 , init: []int{len: 2}}

println(typeof(coordinates_2d).name)  // [][]int

println(coordinates_2d) // [[0, 0], [0, 0], [0, 0], [0, 0]]

我们初始化了一个可变的二维整数数组,由类型[][]int表示。我们通过将len属性设置为4来使用名为coordinates_2d的变量初始化数组。然后,我们使用init: []int{len: 2}语句初始化了每个长度为2的4个数组。由于我们设置了 init 属性,因此第二维中的4个数组将被初始化为具有2个元素的默认整数值0。

让我们检查coordinates_2d二维数组的长度:

println(coordinates_2d.len) // 输出:4

让我们定义长度为2的数组,它们是一个单位长度正方形的点坐标:

point_1 := [0, 0]

point_2 := [0, 1]

point_3 := [1, 0]

point_4 := [1, 1]

指定了长度为4的二维数组,所以我们已经使用 len:4 语句定义了它。因此,它可以容纳4个数组,每个数组都有2个元素的长度。让我们使用=符号将坐标值分配给coordinates_2d二维数组,其中四个坐标分别是point_1point_2point_3point_4,如下所示:

coordinates_2d[0] = point_1

coordinates_2d[1] = point_2

coordinates_2d[2] = point_3

coordinates_2d[3] = point_4

println(coordinates_2d) // [[0, 0], [0, 1], [1, 0], [1, 1]]

进行操作: 创建具有四个点坐标的二维数组coordinates_2d,而不是单独从point_1point_4定义数组,如上面的代码所示。这种方法的代码如下所示:

coordinates_2d = [
    [0, 0],
    [0, 1],
    [1, 0],
    [1, 1],
]

请注意,即使在向现有多维数组添加项的这种方法中,我们仍然使用=符号后跟[开放方括号,而且每个元素都是长度为2的整数数组的表示形式,如先前声明coordinates_2d多维数组时所定义的一样。在指定了各个元素之后,然后我们使用]关闭方括号来关闭语句。

此外,需要注意的是,在上面的代码中,coordinates_2d数组的最后一个[1,1]元素后面指定了逗号。末尾元素后面的逗号是可选的,可以忽略不计。当您执行文件的代码格式化时,会添加此附加逗号。假设您有一个名为file1.v的文件,其中包含主函数中的上述代码;从命令提示符运行v fmt file1.v命令将格式化您的代码,使代码看起来干净且易读。请注意,在第7章"V中的函数"中,您将学习更多关于函数方面的知识。

同样,您也可以根据给出的二维数组示例创建多维数组。对数组执行各种操作我们可以对数组执行各种操作,例如排序、反转或过滤等。我们将研究最常用的数组函数,包括数组克隆、排序、过滤和映射技术。

克隆数组要将数组复制到另一个数组中,您需要使用可用于要复制的数组的clone函数。使用:=进行传统的将数组分配给新变量的方法是不允许的,因为这种方法将会产生不安全的代码。为了实现这一点,我们使用clone函数,如下面的代码所示:

r := [1,2,3,4]

mut u := r.clone() // 将数组r克隆到变量u中

println(u)

输出如下:

[1, 2, 3, 4]

在上面的代码中,我们可以观察到r不可变数组被克隆为可变数组u。直接将数组分配给新变量将会抛出错误。例如,让我们考虑运行以下代码时会发生什么情况:

s := r // 这将引发错误。

程序将抛出一条错误消息,指出:

error: use array2 := array1.clone() instead of array2 := array1 (or use unsafe) .

另一种复制数组的方法,正如前面的错误消息建议的那样,是使用unsafe将一个数组复制到一个新变量中:

s := unsafe { r }

println(s)

输出如下:

[1, 2, 3, 4]

现在,r数组已成功使用unsafe块分配给一个新的s变量。

排序数组可以使用可用于数组的sort函数对数组进行排序。默认情况下,应用于数组的sort函数将按升序排列数组的元素。排序操作更改了数组元素的原始顺序。因此,只能在可变数组上进行排序。

sort函数可选地接受一个排序表达式作为输入参数。通常,排序表达式指示需要按何种顺序对元素进行排序。排序表达式使用两个特殊的变量ab以及><运算符之一构建:

特殊变量ab表示数组的元素。

<运算符用于按升序对数组元素进行排序。

>运算符用于按降序或反向顺序对数组元素进行排序。

在理解了排序数组的基本原则之后,我们将演示如何将排序应用于具有整数、字符串和结构类型元素的数组,并提供示例。

按整数数组元素的大小对元素进行排序的代码如下所示:

mut i := [ 3, 2, 8, 1]

i.sort() // 按升序排列

println(i)

i.sort(a > b) // 按降序排列

println(i)

输出如下:

[1, 2, 3, 8]

[8, 3, 2, 1]

另外,您可以显式指定排序表达式为 i.sort(a < b),以升序排列数组元素。

对字符串数组进行排序

现在,让我们对一个字符串数组进行排序,并查看结果如何:

mut fruits := ['Apples', 'avocado', 'banana', 'Orange']

fruits.sort() // 升序

println(fruits)

fruits.sort(a>b) // 逆序 

println(fruits)

下面是输出:

['Apples', 'Orange', 'avocado', 'banana']

['banana', 'avocado', 'Orange', 'Apples']

请注意,升序根据ASCII序列对字符串进行排序。

对带有结构体元素的数组进行排序

当任何数组的元素具有结构体时,这些特殊的 ab 变量的目的将派上用场。在这种情况下,我们可以扩展排序语句,根据结构体字段对数组元素进行排序。

例如,让我们考虑以下代码,演示如何对元素为结构体类型的数组进行排序:

module main

struct Student {
        id    int
        name  string
        class int
}

fn main() {

        // 声明一个空数组
        mut students := []Student{}

        // 创建学生
        st1 := Student{
                id: 1
                name: 'Ram'
                class: 9
        }

        st2 := Student{
                id: 2
                name: 'Katy'
                class: 3
        }

        st3 := Student{
                id: 3
                name: 'Tom'
                class: 6
        }

        // 将所有学生添加到数组中
        students << [st1, st2, st3]
        println(students)

        // 按 id 反向排序学生
        students.sort(a.id > b.id)
        println('按 id 反向排序的学生:')
        println(students)

        // 按班级升序排列学生
        students.sort(a.class < b.class)
        println('按班级升序排序的学生:')
        println(students)

        // 按名称反向排序学生
        students.sort(a.name > b.name)
        println('按名称反向排序的学生:')
        println(students)

}

我们创建了一个名为 Student 的结构体,其中包含三个结构体字段:idnameclass。然后,我们创建了三个学生并将这些学生添加到一个数组中。然后,我们根据 idnameclass 字段对学生进行了排序。对 Student 结构体数组执行的排序操作结果如下所示:

[Student{
    id: 1
    name: 'Ram'
    class: 9
}, Student{
    id: 2
    name: 'Katy'
    class: 3
}, Student{
    id: 3
    name: 'Tom'
    class: 6
}]

id 反向排序的学生:

[Student{
    id: 3
    name: 'Tom'
    class: 6
}, Student{
    id: 2
    name: 'Katy'
    class: 3
}, Student{
    id: 1
    name: 'Ram'
    class: 9
}]

按班级升序排序的学生:

[Student{
    id: 2
    name: 'Katy'
    class: 3
}, Student{
    id: 3
    name: 'Tom'
    class: 6
}, Student{
    id: 1
    name: 'Ram'
    class: 9
}]

按姓名反向排序的学生:

[Student{
    id: 3
    name: 'Tom'
    class: 6
}, Student{
    id: 1
    name: 'Ram'
    class: 9
}, Student{
    id: 2
    name: 'Katy'
    class: 3
}]

筛选数组

V 允许您使用一个名为 filter 的函数来过滤数组,该函数已访问数组变量。filter 函数接受一个过滤条件,该过滤条件投影在数组的每个元素上。条件包含一个表达式和内置的 it 变量,用于表示正在针对条件进行评估的元素,随后是过滤条件。该过滤器根据过滤条件生成一个新的部分数组,并且不会作用于应用了过滤器的原始数组中。因此,可以在可变和不可变数组上应用过滤函数。只有满足过滤条件中提到的逻辑的元素才会传递给过滤后的数组。 数组的过滤操作的结果总是返回一个与操作所应用的数组元素相似类型的数组。

例如,如果我们想在给定的整数数组中过滤所有3的倍数,我们可以使用filter函数应用过滤器,如下所示:

 f := [123456789]

 multiples_of_3 := f.filter(it3 == 0)

 println(multiples_of_3)// [3、6、9]

filter函数也接受匿名函数。值得注意的是,这种带有匿名函数的过滤器可能需要更长的执行时间。让我们考虑以下代码:

fruits := ['apple''mango''water melon''musk melon']

fruits_starting_m := fruits.filter(fn (f string) bool {
    return f.starts_with('m')
})

println(fruits_starting_m)

以下是输出:

['mango'、'musk melon']

前面的代码将过滤器应用到水果数组上,因此如果水果名称以字母m开头,则添加到数组fruits_starting_m中。

map函数应用到数组元素上

V允许您映射任何数组的每个元素,以便产生另一种形式的新数组。

与接受过滤条件并仅对满足过滤条件的数组元素起作用的过滤器函数不同,map应用于数组的所有元素。 map函数可用于接受要在数组每个元素上执行的操作的数组变量作为输入参数。进行映射操作期间数组的每个元素都由内置it变量引用。

与过滤器函数不同,map函数的结果可以产生包含类型相似或不同于执行map函数的数组的元素的数组。

让我们考虑以下示例,将敬语附加到字符串数组的每个名称中:

visitor:=['Tom''Ram''Rao']

res:= visitor.map('Mr.$it')

println(res)

以下是输出:

['Mr.Tom'、'Mr.Ram'、'Mr.Rao']

数组上的map函数也可以接受匿名函数作为输入参数。例如,考虑使用带有匿名函数的数组的map函数的以下代码:

colors := ['red''blue''green''white''black']

colors_with_letter_e := colors.map(
    fn (c string) int {
    if c.contains('e')return { 1
    } else {
        return 0
    }
})

println(colors_with_letter_e)

这里,我们正在射具有字母'e'的颜色名称的数组。然后,我们构建一个整数数组作为map函数的结果,其中位置表示字母e的存在为1,否则为0。现在我们已经学习了关于数组的所有内容,让我们继续学习有关地图的知识。

映射

映射用于表示键值对的集合。键需要是原始数据类型。使用map关键字定义映射。默认情况下,映射是不可变的。我们可以使用mut关键字定义可变映射。

与其他编程语言(例如C#的Dictionary、Java的HashMap和Python的dict)中的字典类型相似。我们将探讨如何使用映射,包括初始化映射的各种方法。然后,我们将看看如何从映射中添加、更新或删除键值对。

显式初始化映射

以下代码呈现了在V中定义映射的语法:

mut MAP_NAME := map[KEY_TYPE]VALUE_TYPE{}

上述语法显示了一个空可变映射的显式初始化。这里,MAP_NAME是映射的变量名称,遵循标准的变量命名约定。然后,在:=号的右侧,我们使用了map关键字。 KEY_TYPE必须是原始数据类型,如stringrunevoidptr或数值类型。 KEY_TYPE必须在[]方括号之间表示。在指定键类型之后,我们需要提到值类型,然后是空的{}花括号。 显式初始化不支持在{}之间传递键值对作为参数,

我们可以使用以下语法来初始化映射:

MAP_NAME [KEY_1] = VALUE_1
MAP_NAME [KEY_2] = VALUE_2
.
MAP_NAME [KEY_N] = VALUE_N

请注意,映射的值可以通过在 []方括号之间提到键,并在=符号后面的表达式右侧指定值来进行初始化。

为了演示映射的用法,让我们声明一个书籍映射,它将书名作为键和页数作为值:

mut books := map[string]int{}

我们定义了一个可变书籍映射,其中键保存书名,值是int类型以存储页数。 现在,让我们向我们的books映射添加几本书,如下所示:

books ['V on Wheels'] = 320

books ['Go for Dummies'] = 279

println(books)

以下是输出:

{'V on Wheels': 320, 'Go for Dummies': 279}

映射的简短语法初始化

您还可以使用简短的语法在声明期间使用键值初始化映射,如下所示:

mut MAP_NAME := map{
    KEY_1:VALUE_1
    KEY_2:VALUE_2
    .
    KEY_N:VALUE_N
}

上述语法演示了如何使用变量定义可变映射,命名为MAP_NAME,遵循V的标准变量命名约定。 然后,值在:=号的右侧进行初始化,该号以接受每行指定的各种键值对的映射关键字开头。 这些键值对包含在{}花括号中。 如果您在单行中定义映射,请使用逗号符分隔键值对。

让我们创建一个学生在各科获得的分数映射:

mut student_1 := map{
    '英语'90'数学'96'物理'83'化学'89
}

println(student_1)

以下是输出:

{'english': 90, 'mathematics': 96, 'physics': 83, 'chemistry': 89}

映射中键值对的计数

您可以使用len检查映射变量上提供的只读属性的键值对总数。

让我们打印我们之前定义的student_1映射中键值对的总数:

cnt:= student_1.len

println('student_1映射中有$cnt个键值对')

以下是输出:

在student_1映射中有3个键值对

检索映射的键的值

假设我们想从student_1映射中检索物理学科的分数。 我们可以编写以下代码:

println(student_1 ['physics'])// 83

从映射中访问不存在的键

如果我们尝试访问映射中不存在的键,则如果值是整数类型,则返回0;如果是字符串,则返回空字符串:

println(student_1 ['geography'])// 0

在上面的代码中,我们试图访问student_1映射中不存在的地理学科的分数。

处理映射中检索缺少键的值

我们可以优雅地处理从映射中检索到的键未找到的情况,使用or {}块并显示直观的错误消息。

让我们考虑同样的情况,当我们尝试访问地理学科的分数时; 除了返回0之外,它由V执行,我们可以使用or {}块显示详细的错误消息:

sub := 'geography'

res := student_1[sub] or {panic('marks for subject $sub not yet updated')} // throws error

以下是输出:

V panic:marks for subject geography not yet updated

因此,我们可以使用or {}块自定义搜索映射时未找到的键的行为。

更新映射中键的值

您可以通过指定已存在的键后跟值并在=号之后指定值来更新映射中键的值。

为了演示如何更新映射的键值对的值,让我们考虑学生(student_1)的映射。我们将把英语科目的分数从90更新到93:

student_1['english'] = 93

println(student_1)

以下是输出:

{'english': 93, 'mathematics': 96, 'physics': 83, 'chemistry': 89}

从可变映射中删除键值对

您可以使用映射上可用的delete函数从可变映射中删除键值对。 delete函数接受需要删除的映射的键并使用结果更新映射变量。

例如,如果我们想从student_1映射中删除物理学科的分数,则可以编写以下语句:

println('删除键前的键值对:$student_1.len')

student_1.delete('physics')

println('删除键后的键值对:$student_1.len')

以下是输出:

删除键前的键值对:4

删除键后的键值对:3

在从student_1映射中删除物理学键之后,长度属性减少了1个,并且student_1的新长度为3。

总结

在本章中,我们详细了解了数组和映射的工作原理。关于数组,我们了解了不同类型的数组,包括固定大小的数组和多维数组,并执行了各种操作,例如克隆、排序和过滤数组。

我们还学习了映射的初始化方式,并执行了各种映射操作技术,例如计算键值对的数量和检索给定键的值。我们还学习了如何操作映射的键值对,包括更新和删除键值对。

在学习V编程的基本概念过程中,了解如何使用逻辑或关系运算符将代码分支执行不同的任务以及如何递归执行操作至关重要。 V将使用条件语句(例如 if )来帮助编写此类代码,并使用迭代语句进行重复操作或对给定数组或映射中的一堆项目进行迭代。因此,让我们进入下一章《条件语句和迭代语句》,以了解更多有关这些概念的内容。