Skip to content

Latest commit

 

History

History
184 lines (142 loc) · 6.9 KB

#007_数组方法.md

File metadata and controls

184 lines (142 loc) · 6.9 KB

2.5 数组方法

2.5.1 基本方法

  • println(arr) 打印数组

  • s := arr.str() 数组转换成字符串

  • arr_copy := arr.clone() 复制数组

  • .filter() 筛选数组

      nums := [1, 2, 3, 4, 5, 6]
      even := nums.filter(it % 2 == 0)
      println(even) // [2, 4, 6]
      // filter can accept anonymous functions
      even_fn := nums.filter(fn (x int) bool {
          return x % 2 == 0
      })
      println(even_fn)
    
  • .map() 映射数组

      words := ['hello', 'world']
      upper := words.map(it.to_upper()) // it refers to the current element
      println(upper) // ['HELLO', 'WORLD']
      // map can also accept anonymous functions
      upper_fn := words.map(fn (w string) string {
          return w.to_upper()
      })
      println(upper_fn) // ['HELLO', 'WORLD']
    
  • .any()/.all() 检测数组

      nums := [1, 2, 3]
      println(nums.any(it == 2)) // true
      println(nums.all(it >= 2)) // false
    
  • a.repeat(n) 复制数组 n 次并拼接

  • a.insert(i, val)i 位置插入 val,其余值右移

  • a.insert(i, [3, 4, 5])i 位置插入若干元素

  • a.prepend(val) 在开头插入元素,等价于 a.insert(0, val)

  • a.prepend(arr) 在开头插入数组

  • a.trim(new_len)new_length < a.len 时截断数组

  • a.clear() 清空数组但不改变容量,等价于 a.trim(0)

  • a.delete_many(start, size) 从索引 start 开始连续删除 size 个元素–会触发重新分配

  • a.delete(index) 等价于 a.delete_many(index, 1)

  • a.delete_last() 删除最后一个元素

  • a.first() 等价于 a[0]

  • a.last() 等价于 a[a.len - 1]

  • a.pop() 删除并返回最后一个元素

  • a.reverse() 创建一个新的数组

  • a.reverse_in_place()a 中元素逆序,直接存在 a

  • a.join(joiner) 用字符串 joiner 作为分隔符将字符串数组拼接成一个字符串

进一步了解参考 vlib/arrays

2.5.2 数组排序

普通排序

对各种数组进行排序非常简单直观。使用特殊变量 a 和 b 来提供自定义的排序条件。

mut numbers := [1, 3, 2]
numbers.sort() // 1, 2, 3
numbers.sort(a > b) // 3, 2, 1

对于结构体数组:

struct User {
    age  int
    name string
}

mut users := [User{21, 'Bob'}, User{20, 'Zarkon'}, User{25, 'Alice'}]
users.sort(a.age < b.age) // sort by User.age int field
users.sort(a.name > b.name) // reverse sort by User.name string field

自定义排序

V 还支持通过 sort_with_compare 方法自定义排序。这需要一个定义排序顺序的比较函数。用于通过自定义排序规则同时对多个字段进行排序。下面的代码按 name 升序和 age 降序对数组进行排序。

struct User {
    age  int
    name string
}

mut users := [User{21, 'Bob'}, User{65, 'Bob'}, User{25, 'Alice'}]

custom_sort_fn := fn (a &User, b &User) int {
    // return -1 when a comes before b
    // return 0, when both are in same order
    // return 1 when b comes before a
    if a.name == b.name {
        if a.age < b.age {
            return 1
        }
        if a.age > b.age {
            return -1
        }
        return 0
    }
    if a.name < b.name {
        return -1
    } else if a.name > b.name {
        return 1
    }
    return 0
}
users.sort_with_compare(custom_sort_fn)

2.5.3 数组切片

普通切片

切片就是父数组的一部分。最原始的定义为:指代由 .. 操作符分隔的两个索引值之间的一系列元素,左闭右开。右边的索引值必须大于等于左边的索引值。

若右索引值缺失则默认为数组长度,若左索引值缺失默认为 0。

nums := [0, 10, 20, 30, 40]
println(nums[1..4]) // [10, 20, 30]
println(nums[..4]) // [0, 10, 20, 30]
println(nums[1..]) // [10, 20, 30, 40]

在 V 中切片就是数组本身(它们不是不同的数据类型)。所以所有的数组操作都可以用在切片上。比如可以将同类型的数组 push 到切片上:

array_1 := [3, 5, 4, 7, 6]
mut array_2 := [0, 1]
array_2 << array_1[..3]
println(array_2) // `[0, 1, 3, 5, 4]`

刚创建时切片相应元素地址与父数组一致。不过无论父数组设置如何,切片总是以最小容量创建,即 cap == len。这会导致,当切片大小增加时,它会立即重新分配内存并复制到另一个内存位置,从而独立于父数组(增大时复制)。特别注意的是,将元素 push 到切片不会改变父数组:

mut a := [0, 1, 2, 3, 4, 5]
mut b := a[2..4]
b[0] = 7 // `b[0]` is referring to `a[2]`
println(a) // `[0, 1, 7, 3, 4, 5]`
b << 9
// `b` has been reallocated and is now independent from `a`
println(a) // `[0, 1, 7, 3, 4, 5]` - no change
println(b) // `[7, 3, 9]`

向父数组添加元素可能也可能不会改变其与子切片的独立性。结果取决于父数组的容量,并且是可预见的:

mut a := []int{len: 5, cap: 6, init: 2}
mut b := a[1..4]
a << 3
// no reallocation - fits in `cap`
b[2] = 13 // `a[3]` is modified
a << 4
// a has been reallocated and is now independent from `b` (`cap` was exceeded)
b[1] = 3 // no change in `a`
println(a) // `[2, 2, 2, 13, 2, 3, 4]`
println(b) // `[2, 3, 13]`

如果你想直接创建一个与父数组独立的切片,可以调用 .clone() 方法:

mut a := [0, 1, 2, 3, 4, 5]
mut b := a[2..4].clone()
b[0] = 7 // NB: `b[0]` is NOT referring to `a[2]`, as it would have been, without the .clone()
println(a) // [0, 1, 2, 3, 4, 5]
println(b) // [7, 3]

负索引切片

V 支持负索引的数组或字符串切片。负索引指从数组末尾往前数,例如 -3 等于 array.len() - 3。但负索引切片与正常切片句法不同,需要在数组名与方括号之间增加 gate,像这样 a#[..-3]gate 表明这是一种不同类型的切片,你应该记住结果在数组中“锁定”了。返回的切片尽管始终是有效数组,但它可能为空:

a := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
println(a#[-3..]) // [7, 8, 9]
println(a#[-20..]) // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
println(a#[-20..-8]) // [0, 1]
println(a#[..-3]) // [0, 1, 2, 3, 4, 5, 6]

// empty arrays
println(a#[-20..-10]) // []
println(a#[20..10]) // []
println(a#[20..30]) // []

2.5.4 数组方法的链式调用

你可以链式调用数组的方法,比如 .filter().map(),并使用 it 这一内置变量得到一个经典的 map/filter 功能示例:

// using filter, map and negatives array slices
files := ['pippo.jpg', '01.bmp', '_v.txt', 'img_02.jpg', 'img_01.JPG']
filtered := files.filter(it#[-4..].to_lower() == '.jpg').map(it.to_upper())
// ['PIPPO.JPG', 'IMG_02.JPG', 'IMG_01.JPG']