07/09/2018, 17:21

Swift - Custom operators - Part 1

Swift - Custom operators - Part 1 Trong phần này, các bạn sẽ được tự chế tạo operator cho riêng mình. Nó sẽ biểu diễn cho 1 loạt function phức tạp lặp đi lặp lại nhiều lần. Ví dụ nếu bạn có phép tính 2 nhân 2 nhân 2 v..v thì tại sao không có phép tính luỹ thừa cho gọn. Trước tiên, mình nên làm 1 ...

Swift - Custom operators - Part 1

Trong phần này, các bạn sẽ được tự chế tạo operator cho riêng mình. Nó sẽ biểu diễn cho 1 loạt function phức tạp lặp đi lặp lại nhiều lần. Ví dụ nếu bạn có phép tính 2 nhân 2 nhân 2 v..v thì tại sao không có phép tính luỹ thừa cho gọn. Trước tiên, mình nên làm 1 type cố định rồi khi nào quen rồi mình mở rộng bằng cách dùng generic.

1. Lý thuyết về các loại toán tử:

a. Toán tử một ngôi (unary): được dùng với duy nhất 1 toán hạng hay một tiền tố. Chỗ này mình phân tích thêm chút, nó bao gồm:

  • Postfix: Những toán tử đứng sau, hay gặp nhất là a!, b?
  • Prefix: Những toán tử đứng trước ví dụ như !true, ++a

b. Toán tử hai ngôi (binary): được dùng với 2 toán hạng. Các toán tử +, -, x , % , hay toán tử so sánh ==, !== hay logic &&, || đều thuộc về phần này.

  • Infix là một tên gọi khác của toán tử 2 ngôi này, vì nó nằm giữa.

c. Toán tử ba ngôi (ternary): được dùng với 3 toán hạng. Chắc các bạn còn nhớ đến toán tử điều kiện ? chứ. ví dụ: (a == b ? true : false).

1.Ví dụ: Toán tử luỹ thừa
Đặt toán tử luỹ thừa là dạng toán từ mà mình tự custom, nên ta chọn đặt tên thoải mái. Cách đặt thường là sẽ kết hợp các ký tự đặt biết mà phải có ý nghĩa 1 chút. Ví dụ, luỹ thừa là nhiều phép nhân, nên ta đặt: ** hoặc các bạn đặt là ^ cũng được.

Swift không cho phép ta custom ternary operator

Cái ta có thể custom là các phần còn lại như prefix, postfix, infix. Phép luỹ thừa là cần đến 2 toán hạng nên ta dùng infix. Code như sau:

infix operator **

func **(lhs: Int, rhs: Int) -> Int {
  var result = lhs
  for _ in 2...rhs {
    result *= lhs
  }
  return result
}

infix: dạng type của operator cần custom
operator: keyword
**: cái mình muốn tạo ra
tiếp theo là func mà mình muốn định nghĩa cho phép nhân đó. Một lưu ý các bạn để ý đến đoạn result *= lhs, cũng là một phép nhân rồi gắn, kiểu kiểu như custom operator nhưng được Swift tạo ra trước đó.

Note: Mình sẽ dùng wildcard pattern để discard những giá trị loop bằng kỹ thuật "Pattern Matching". Các bạn xem thêm ở đây.

Bây giờ, test thử code:

let base = 2
let exponent = 2
let result = base ** exponent

2. Toán tử kết hợp với phép gắn:
Như mình đã lưu ý ở phần trên, thì mọi operator mình làm thì thường phải có thêm phần compound assignment (kết hợp luôn phép gắn). Xem phần code sau:

infix operator **=
func **=(lhs: inout Int, rhs: Int) {
  lhs = lhs ** rhs
}

Một trong những phần hay mình sẽ phân tích ở đây. Nếu như mình làm bình thường sẽ như ở trên, tức là var result -> xử lý code -> gán lại cho result. Nhưng ở đây mình sẽ dùng inout keyword. Inout là gì, cách dùng như thế nào, các bạn xem ở đây.

Như vậy, bằng việc dùng inout, ta lấy được giá trị sau cùng. Func sẽ thay đổi inout parameter trực tiếp bởi vì nó được truyền thông qua reference.

Đây là thành quả:

 var number = 2
number **= exponent

3. Thách đố nhỏ

Bây giờ mình muốn tạo 1 operator mà 2 argument truyền vào là string và số lần. Qua phép nhân đó, sẽ nhân số string lên n lần. Ví dụ:

let baseString = "abc"
let times = 5
var multipliedString = baseString ** times

Giúp mình với nhé ;)

Tiếp theo mình sẽ đi qua phần hai, những phần Advance hơn về Custom Operator.
Part 2: https://kipalog.com/posts/Swift---Custom-operators---Part-2

0