Tổng quan

Trong Cadence, mảng, từ điển và tùy chọn là ba cấu trúc dữ liệu quan trọng cho phép bạn quản lý các tập hợp giá trị trong mã của mình.

Mảng là tập hợp có thứ tự các giá trị cùng loại. Trong Cadence, mảng được khai báo bằng dấu ngoặc vuông [] và có thể chứa các phần tử thuộc bất kỳ kiểu nào, kể cả các mảng khác.

Từ điển, còn được gọi là map, là tập hợp các cặp Key-Value không theo thứ tự. Trong Cadence, từ điển được khai báo bằng dấu ngoặc nhọn {} và có thể chứa các khóa và giá trị thuộc bất kỳ loại nào.

Tùy chọn(Optional) là một cách để biểu thị các giá trị có thể tồn tại hoặc không tồn tại. Trong Cadence, các tùy chọn được khai báo bằng dấu chấm hỏi ? trước loại giá trị và có thể chứa giá trị hoặc nil, cho biết không có giá trị nào.

Các cấu trúc dữ liệu này cho phép bạn dễ dàng quản lý và thao tác các tập hợp giá trị trong mã Cadence của mình, giúp việc xây dựng các ứng dụng phức tạp và mạnh mẽ trên chuỗi khối Flow trở nên dễ dàng hơn.

Types

Để thực hành về các loại, mở về Flow playground (https://play.onflow.org)

Trong Cadence, đoạn code bạn viết thường có thể hiện một kiểu dữ liệu nào đó. Hay đơn giản type là một cách phân loại dữ liệu cho trình biên dịch hoặc thông dịch hiểu các lập trình viên muốn sử dụng dữ liệu. Hầu hết các ngôn ngữ hỗ trợ nhiều kiểu dữ liệu khác nhau, như số thực, số nguyên hay Boolean. Ví dụ:

var GFI = "isAwsome"

Cadence sẽ tự động nhận ra bạn đã khởi tạo một Chuỗi (String). Tuy nhiên, nếu chúng ta muốn rõ ràng hơn về các loại của mình, chúng ta có thể đưa loại đó vào phần khai báo, như sau:

var GFI: String = "isAwsome"

Việc bao sử dụng kiểu dữ liệu nào đó thường rất hữu ích để chúng ta có thể suy luận về việc tìm những lỗi sai ở đâu trong chương trình của mình. Cadence cũng sẽ nói với bạn rằng bạn đã phạm sai lầm nếu bạn dự định một biến có kiểu khác. Ví dụ:

var GFI: String = 3

Arrays

Vậy mảng là gì? Mảng là một cấu trúc dữ liệu linh hoạt trong lập trình, chứa một tập hợp các phần tử có cùng kiểu dữ liệu. Mỗi phần tử trong mảng được gắn với một chỉ số (thường là số nguyên), cho phép truy xuất nhanh đến phần tử đó trong mảng. Hãy xem xét một mảng String cơ bản trong Cadence:

var people: [String] = ["Alice", "Adam" , "GFI"]

Mảng về địa chỉ ví sẽ có ví dụ như sau:

var addresses: [Address] = [0x1, 0x2, 0x3]

Chúng ta cũng có thể lập chỉ mục thành các mảng để xem các phần tử là gì. Điều này hoàn toàn giống Javascript hoặc các ngôn ngữ tương tự.

var addresses: [Address] = [0x1, 0x2, 0x3]
log(addresses[0]) // 0x1
log(addresses[1]) // 0x2
log(addresses[2]) // 0x3

Thêm dữ liệu vào mảng

append(_ element: Type)

(lưu ý rằng element_ ở phía trước, có nghĩa là nó ẩn, vì vậy bạn không phải đặt nhãn đối số khi gọi hàm. Vì vậy, thay vì .append(element: value), bạn có thể chỉ cần làm .append(value))

Thêm một phần tử vào cuối mảng.

var people: [String] = ["Alice", "Adam", "GFI"]
people.append("Hasaki")
log(people) // ["Alice", "Adam", "GFI", "Hasaki"]

contains(_ element_: Type): Bool

Kiểm tra xem trong mảng có chứa phần tử hay không

var people: [String] = ["Alice", "Adam", "GFI"] 
log(people.contains("GFI")) // true 
log(people.contains("Poop")) // false

remove(at: Int)

Xóa phần tử theo thứ tự (Phần tử đầu tiên bắt đầu từ số 0)

var people: [String] = ["Alice", "Adam", "GFI"]
people.remove(at: 1)
log(people) // ["Alice", "GFI"]

length

Trả về số lượng phần tử trong mảng

var people: [String] = ["Alice", "Adam", "GFI"] 
log(people.length) // 3

Dictionaries

Trong lập trình, từ điển (dictionaries) là một dạng dữ liệu lưu trữ đối tượng có cấu trúc keyvalue, trong đó mỗi key được liên kết với một giá trị (value) tương ứng. Bạn có thể truy cập giá trị của một key bằng cách truyền key vào từ điển. Ví dụ:

var names: {String: String} = {"Hoang": "Hai", "Minh": "Nha", "Ngoc": "Dung"}

Trong ví dụ trên, chúng ta đã map giữa StringString. Cụ thể hơn, chúng ta đã map tên của ai đó thành họ của họ. Chúng ta đã làm điều này với loại từ điển, đó là {Type:Type}. Để hiện thị, chúng ta sẽ làm như sau:

var names: {String: String} = {"Hoang": "Hai", "Minh": "Nha", "Ngoc": "Dung"}
log(names["Hoang"]) // "Hai"
log(names["Minh"]) // "Nha"
log(names["Ngoc"]) // "Dung"

Hãy xem một ví dụ về map giữa Strings với Ints.

var soyeuthich: {String: Int} = {"Hoang": 1, "Minh": 20, "Ngoc": 32} 
log(soyeuthich["Minh"]) // 20

insert(key: Type, _ value: Type)

(lưu ý value là ẩn, nhưng key thì không)

Ví dụ:

var soyeuthich: {String: Int} = {"Hoang": 1, "Minh": 20, "Ngoc": 32} 
soyeuthich.insert(key: "Justin Bieber", 1)
log(soyeuthich) // {"Hoang": 1, "Minh": 20, "Ngoc": 32, "Justin Bieber": 1}

remove(key: Type): Type?

Xóa bỏ key và giá trị tương ứng, và trả về giá trị đó

var soyeuthich: {String: Int} = {"Hoang": 1, "Minh": 20, "Ngoc": 32} 
let removedNumber = favouriteNums.remove(key: "Hoang")
log(soyeuthich) // {"Minh": 20, "Ngoc": 32}
log(removedNumber) // 1

keys: [Type]

Trả về một mảng gồm tất cả các khóa trong từ điển.

var soyeuthich: {String: Int} = {"Hoang": 1, "Minh": 20, "Ngoc": 32}
log(soyeuthich.keys) // ["Hoang", "Minh", "Ngoc"]

values: [Type]

Trả về một mảng gồm tất cả các giá trị trong từ điển.

var soyeuthich: {String: Int} = {"Hoang": 1, "Minh": 20, "Ngoc": 32} 
log(soyeuthich.values) // [1,20,32]
flow dictionaries
flow dictionaries

Optionals

Các optionals RẤT quan trọng, nhưng có thể phức tạp. Bạn có thể sẽ gặp các optionals trong mọi việc bạn làm trong Cadence. Hầu hết thời gian, nó sẽ là do từ điển.

Loại optional type trong Cadence được biểu thị bằng dấu ?. Nó có nghĩa là: “Đó là loại dữ liệu, hoặc không”. Hãy cùng xem:

var name: String? = "GFI"

Lưu ý ? sau String. Điều đó có nghĩa là: “tên biếnString hoặc nil” Rõ ràng, chúng ta biết đó là một String vì nó bằng với “GFI”. Nhưng chúng ta cũng có thể có như thế này:

var name: String? = nil

Điều này sẽ không có bất kỳ lỗi biên dịch nào, bởi vì nó đúng. Một String? có thể là nil.

Force-Unwrap Operator

Điều này đưa chúng ta vào toán tử force-unwrap, !. Toán tử này “mở” một loại tùy chọn bằng cách: “Nếu thứ này bằng nil, PANIC! Nếu nó không bằng nil, chúng ta ổn, nhưng hãy loại bỏ optional type”:

var name1: String? = "GFI"
var unwrappedName1: String = name1! // lưu ý nó loại bọ optional type

var name2: String? = nil
var unwrappedName2: String = name2! // PANICS! Toàn bộ chương trình sẽ bị hủy bỏ vì nó phát hiện ra sự cố. Nó đã cố unwrap nil, điều này không được phép

Optionals và Dictionaries

Được rồi, đây là nơi chúng ta sẽ kết hợp mọi thứ để nói về Optionals và Dictionaries. Trước đây, khi ta giải thích về từ điển, chúng ta đã bỏ sót một thông tin quan trọng (không có ý định chơi chữ): Khi bạn truy cập các phần tử của từ điển, nó sẽ trả về giá trị dưới dạng optional. Điều đó nghĩa là gì? Hãy xem bên dưới:

Giả sử chúng ta có từ điển này:

let thing: {String: Int} = {"Hi": 1, "Bonjour": 2, "Hola": 3}

Bây giờ, giả sử chúng ta muốn ghi giá trị được map với “Bonjour”:

let thing: {String: Int} = {"Hi": 1, "Bonjour": 2, "Hola": 3}
log(thing["Bonjour"]) // this will print 2

Nó sẽ in 2 như chúng ta đã trình bày ở trên. Vì vậy, nó không có gì lạ. Nhưng nó thực sự là hãy tạo một kịch bản mới giống như sau:

pub fun main(): Int {
    let thing: {String: Int} = {"Hi": 1, "Bonjour": 2, "Hola": 3}
    return thing["Bonjour"] // ERROR: "Mismatched types. expected `Int`, got `Int?`"
}

Điều này sẽ cho chúng ta một LỖI! Lỗi cho biết: “Các loại dữ liệu sẽ không khớp. Mong đợi Int, có Int?“. Chúng ta đã biết Int? là gì? Nó có nghĩa là nó là một optional, vì vậy nó có thể là Int hoặc nó có thể là nil. Để sửa lỗi này, chúng ta phải sử dụng toán tử force-unwrap !, như sau:

pub fun main(): Int {
    let thing: {String: Int} = {"Hi": 1, "Bonjour": 2, "Hola": 3}
    return thing["Bonjour"]! // we added the force-unwrap operator
}

Returning Optionals vs. Unwrapping

Có trường hợp nào mà tôi muốn trả kết quả về là Optional thay vì force-unwrapping optional không? Câu trả lời là có. Trên thực tế, hầu hết các trường hợp, ta muốn kết quả được trả lại là Optional thay vì unwrapping. Ví dụ:

pub fun main(): Int {
    let thing: {String: Int} = {"Hi": 1, "Bonjour": 2, "Hola": 3}
    return thing["Bonjour"]! // we are force-unwrapping the optional
}

… điều này sẽ gây Panic và hủy bỏ chương trình nếu không có giá trị tại phím “Bonjour”. Thay vào đó, chúng ta có thể viết như thế này:

pub fun main(): Int? { // notice the return value is an optional type
    let thing: {String: Int} = {"Hi": 1, "Bonjour": 2, "Hola": 3}
    return thing["Bonjour"] // we leave the optional
}
Optionals
Optionals

Bằng cách này, người gọi có thể xử lý trường hợp giá trị trả về bằng nil, thay vì phải lo lắng về lỗi trong chương trình. Logic tương tự này cũng áp dụng cho các chức năng khác trong mã Cadence của bạn.

Kết luận

Điểm nổi bật chính là khi truy cập các giá trị của từ điển, bạn sẽ luôn nhận lại các optionals. Vì vậy, nếu bạn muốn loại dữ liệu thực tế chứ không phải opitonals, bạn phải “unwrap” nó bằng cách sử dụng toán tử force-unwrap !