+1

Custom Subscripting in Swift Tutorial (translate)

Chào các bạn! Đến hẹn lại lên, lại cong mông lên viết bài báo cáo cho kịp tiến độ. Lần này mình sẽ dịch bài viết Custom Subscripting In Swift Tutorial trên reywenderich. Do tiếng anh mình còn hạn chế nên có gì thiếu sót mong ae thông cảm.

Subscripting bắt đầu có từ xcode 4.4 và quay lại vào giữa năm 2012.Khi đó Swift đang trong quá trình phát triển 2 năm. Như với nhiều tính năng mạnh mẽ khác trong Objective C, như ARC, subscripting cũng đã trở thành một phần của Swift. Subscripts không thực sự thêm chức năng cho class, thay vào đó nó mang đến cho chúng ta cách truy cập nhanh hơn đến phần tử của mảng. Nghe có vẻ giống như một ứng dụng đơn giản, nhưng subscript tăng cường đáng kể việc thu gọn code, làm cho nó dễ đọc và tiện lợi hơn. Ví dụ. trc đây để truy cập vào mang ta phải dùng myArray.objectAtIndex(2). Nhưng từ xcode 4.4 bạnbạn dùng myArray[2] là đc. Còn với kiểu dữ liệu Dictionary trc đây bạn dùng value = [myDictionary objectForKey:@"VotingAge"]. Bây giờ bạn có thể dùng myDictionary[@"VotingAge"]. Nhưng nói thật mình vẫn quen dùng objectForKey hơn 😄. Hơn nữa, Objective-C hỗ trợ thêm subscript để custom class, nhưng nó đòi hỏi một số nỗ lực. Ngược lại, Swift làm cho việc thêm subscript đến các class dễ dàng hơn bao giờ hết, đó là lời khen của Apple đấy. Trong bài viết này, bạn sẽ khám phá subscript bằng cách xây dựng một trò chơi cờ đơn giản. Bạn sẽ thấy cách dễ dàng di chuyển phần xung quanh là, minh họa sức mạnh của subscripting ngay trên bảng trò chơi. Để bắt đầh project bạn cần download resource tại đây:

http://cdn2.raywenderlich.com/wp-content/uploads/2014/10/StarterSubscriptPlayground.playground.zip

Bạn nhìn qua 2 loại đã có sẵn trong project:

  • enum PlayerColor: Đại diện cho màu sắc của một mảnh trên boảrd. Lưu ý rằng một mảnh với PlayerColor loại .None đại diện cho một không gian trống trên bảng.
  • class GameBoard: Đây sẽ là class chính, bạn sẽ làm việc với nó. Nó có 1 mảng 2 chiều chứa dữ liệu.

GameBoard điều khiển tất cả các trò chơi và theo dõi các vị trí mảnh, bạn cần thêm các function để add các subscripting vào gameboard. Bạn hãy mở cửa sổ Assistant Editor bằng cách vào từ View\Assistant Editor\Show Assistant Editor. Sau đó gõ 2 dòng lệnh bên dưới vào vào gameboard class:

let board = GameBoard()
board.displayBoard()

Sau khi khỏi tạo xong 1 íntance của class GameBoard. sau đó bạn gọi lệnh board.displayBoard() sẽ in ra cửa sổ bên console.

-r-r-r-r
r-r-r-r-
-r-r-r-r
--------
--------
b-b-b-b-
-b-b-b-b
b-b-b-b-

Mãng sẽ gồm 8 yếu tố. Trong thực tế, điều này làm cho nó thành một mảng hai chiều.Ví dụ, board[2][5] đề cập đến yếu tố thứ sáu của mảng con thứ ba - hãy nhớ rằng yếu tố đầu tiên của một mảng là lúc chỉ số 0). Lưu ý: kí hiệu board là y-befor-x. Tức là (7,0) có nghĩa là cột thứ 7 và hàng 0. Nghe có vẻ hơi ngược nhỷ. Vì vậy, để xác định vị trí các mảnh tại vuông (7,0), bạn sẽ cần phải truy cập vào bảng [0] [7]. Bạn xem ảnh phía dưới: alt

Implementing a Simple Moving Method Cách hiệu quả để tạo ra 1 subscripts, sau đó dùng nó để thây đổi các chỉ số. Đơn giản chỉ là di chuyện 1 item từ chỗ này đến chỗ khác. toạ độ 1 item sẽ gồm 2 chỉ số: Int's:(int, int) add function này vào cuối class GameBoard:

func movePieceFrom(from: (Int,Int), to: (Int,Int)) {
  let pieceToMove = board[from.1][from.0]
  board[from.1][from.0] = empty()
  board[to.1][to.0] = pieceToMove
}

Đơn giản là bạn lưu trữ các item muốn đi chuyển qua 1 vùng nhớ khác. sau đó bạn thay thế các giá trị của nó vào. Bạn hãy làm thử và thêm 2 dòng sau:

board.movePieceFrom((2,5), to: (3,4))
board.displayBoard()

Ở đây, bạn cho GameBoard của bạn để di chuyển các mảnh tại (2,5), mà sẽ xảy ra là một mảnh màu đen, đường chéo bên phải. Hãy nhìn vào giao diện điều khiển, board cho thấy các mảnh từ (2,5) chuyển đến vị trí mới của mình tại (3,4):

-r-r-r-r
r-r-r-r-
-r-r-r-r
--------
---b----
b---b-b-
-b-b-b-b
b-b-b-b-

Tìm vị trí các Phần tử ở ở console bạn dễ dàng nhận ra vị trí của các item. nhưng chương trình thì nó ko thể có mắt mà nhận định như chúng ta đc. Nó ko nhận dạng đc các item nếu ko truy xuất trực tiếp đến thuộc tính của GameBoard.Bạn thêm function sau vào cuối class GameBoard:

func playerAtLocation(coordinates: (Int, Int)) -> PlayerColor {
  return board[coordinates.1][coordinates.0]
}

Hàm này đơn giản trả về màu sắc của phần tử nằm ở 1 vị trí chỉ định. Trong trường hợp này thiết lập màu sắc tại các vị trí sẽ rất có ích.

func setPlayer(player: PlayerColor, atLocation coordinates: (Int, Int)) {
  board[coordinates.1][coordinates.0] = player
}

Declaring Subscripts Thuộc tính và subscripts có khai báo tương tự nhau:

var computedProperty: Type {
  get {
    //return someValue
  }
  set (newValue) {
    //setSomeValue()
  }
}
subscript(parameters) -> ReturnType {
  get {
    //return someValue
  }
  set (newValue) {
    //setSomeValue()
  }
}

Trong setter newValue là 1 tham số mặc định có giá trị của subscript. Bạn dùng các tham số này để thay đổi giá trị bên trong class của bạn. Bạn ko cần khai báo tham số newValue. Cũng giống như properties, subscript cũng có các thuộc tính như chỉ đọc hoặc đọc ghi. read-only của subscript ko chỉ rõ ràng đọc ghi,toàn bộ nó chỉ là 1 getter.

subscript(parameters) -> ReturnType {
  return someValue
}

alt Một phương pháp phổ biến để subscripting là phải có một phương pháp thông thường mà hoạt động giống như các subscript, cho phép nó hoạt động như một phím tắt để phương pháp này. VD: xem xét rằng NSMutableArray có objectAtIndex () và replaceObjectAtIndex (withObject 😃. Sử dụng subsript trên NSMutableArray để gọi 2 method và sử dụng. VD như objectAtIndexedSubscript: và setObject:atIndexedSubscript:

Creating a Subscript on GameBoard bạn thêm subsript rỗng vào phía dưới class GameBoard **subscript(coordinates: (Int,Int)) -> PlayerColor { get {

} set {

} }**

Bỏ qua các lỗi cho bây giờ, họ sẽ biến mất khi bạn kết hợp playerAtLocation () và setPlayer (atLocation 😃 vào một đọc-ghi subscript. bạn add thêm đoạn code sau vào subsript getter:

get {
  return playerAtLocation(coordinates)
}

cuối của playground bạn add thêm 2 line sau để nhìn thấy thay đổi:

let player = board[(3,4)]
println(player.description)

Tương tự như vậy, thực hiện các setter sử dụng setPlayer (atLocation 😃 để làm cho code của bạn trông như thế này:

 set {
  setPlayer(newValue, atLocation: coordinates)
}

lại vào cuôi class playgorund và gõ vào 2 dòng sau:

board[(3,4)] = .Red
board.displayBoard()

Thì đấy! Các mảnh màu đen ở tọa độ (3,4) trở thành một mảnh màu đỏ!

-r-r-r-r
r-r-r-r-
-r-r-r-r
--------
---r----
b---b-b-
-b-b-b-b
b-b-b-b

Gratuitous Gameplay Lưu ý: Xin chúc mừng, bạn đã học được làm thế nào để thêm subscripting đến các class của riêng bạn trong Swift! Phần còn lại của hướng dẫn này chỉ kéo dài Swift trò chơi này một chút nữa với một số logic gameplay, đó gọi là tùy chọn.

Ngay bây giờ, khi bạn sử dụng chỉ số dưới để thiết lập vị trí mới của một mảnh, các mảnh chỉ đi bất cứ nơi nào bạn nói, thậm chí nếu nó là một động thái bất hợp pháp hay không hợp lệ. Nói về một chuyến đi quyền lực. Bạn cần add thêm 1 vài di chuyển đơn giản. Trước khi nhảy vào code bạn cần phải nghĩ ccsh về cách bạn sẽ alfm để đảm bảo di chuyển hợp lí. alt Đoạn này cũng ko hiểu dịch ntn cho hợp lý nên để nguyên:

  1. Is the new coordinate in the game board? If either of the coordinates are outside the range of 0-7, the move is invalid.
  2. Is the moving piece either red or black? If the piece represents a blank space on the board, there’s no sense in moving it.
  3. Is there a red or black piece at the new location? In checkers, you cannot land on another piece.
  4. Is the piece moving in the right direction? red pieces move down the board, while black pieces move up the board.
  5. Is the new position off by one in both the X-coordinate and the Y-coordinate? Pieces move diagonally by one when they don’t jump over the opponent’s piece.
  6. Does the piece jump over an opponent?. If this is the case, then a blank space must replace the jumped piece on the board.

Bây giờ bạn biết những gì để xác nhận, bạn cần phải thêm những quy định mới cho chương trình. Thay vì làm thay đổi trực tiếp với các subscript, bạn sẽ làm cho những thay đổi trong movePieceFrom (_: to:).

Xoá code bên trong movePieceFrom(_:to:) bạn sẽ thêm các rule từ mảng. Sau khi phát hiện ra 1 di chuyển ko hợp lệ cần phải in ra thông báo lỗi

func error(errorType:String) {
  println("Invalid Move: \(errorType)")
}

Việc kiểm tra đầu tiên là để đảm bảo rằng cả hai vị trí thực sự trên board. Để làm điều này, hãy kiểm tra các tọa độ này nằm trong phạm vi thích hợp của 0-7

if !(0...7 ~= from.0 && 0...7 ~= from.1 && 0...7 ~= to.0 && 0...7 ~= to.1) {
  error("Range error")
  return
}

Toán tử ~= có ý nghĩa kiểm tra để xem nếu phạm vi bên trái chứa các giá trị bên phải. Ở đây, bạn chắc chắn rằng mỗi vị trí của phối hợp là trong khoảng 0-7. Nếu không, bạn gọi các thông báo lỗi và trở về. Tiếp theo bạn đơn giản check chắcd chắn rằng màu sắc củacủa điểm di chuyển ko trống rỗng và trong tình huống này, bạn sẽ sử dụng subscripting để tìm người chơi.

if playerAtLocation(from) == .None {
  error("No Piece to Move")
  return
}

Tương tự như việc xác nhận trước đó, bạn kiểm tra xem có thực sự là một mảnh (của một trong hai màu) tại các vị trí bắt đầu. Hãy add thêm đoạn code vào phần cuối của method movePieceFrom(_:to:):

if playerAtLocation(to) != .None {
  error("Move onto occupied square")
  return
}

Trong trường hợp này. bạn ko muốn vị trí mới là là rỗng. Cần phải đảm bảo các mảnh di chuyển phải đúng hướng. Hãy nhớ rằng mỗi màu chỉ có có 1 đường đi.

let yDifference = to.1 - from.1
if (playerAtLocation(from) == .Red) ? yDifference != abs(yDifference) : yDifference == abs(yDifference) {
  error("Move in wrong direction")
  return
}

yDifference để tìm sự khác biệt giữa các toạ độ y của to và from Nếu mảnh di chuyển là màu đỏ, sau đó nó chỉ có thể di chuyển xuống dưới của board, trong khi 1 mảnh move down nó di move bởi các giá trị giới hạn.

if abs(to.0 - from.0) != 1 || abs(to.1 - from.1) != 1 {
  error("Not a diagonal move")
  return
}
if abs(to.0 - from.0) != 1 || abs(to.1 - from.1) != 1 {
  if abs(to.0 - from.0) != 2 || abs(to.1 - from.1) != 2 {
    error("Not a diagonal move")
    return
  }
  let coordsOfJumpedPiece = ((to.0 + from.0) / 2 as Int, (to.1 + from.1) / 2 as Int)
  let pieceToBeJumped: PlayerColor = self[coordsOfJumpedPiece]
  if contains([.None, playerAtLocation(from)], pieceToBeJumped) {
    error("Illegal jump")
    return
  }
  setPlayer(.None, atLocation: coordsOfJumpedPiece)
}

Bây giờ các bạn check lại lệnh di chuyển và xem kết quả nhé:

let pieceToMove = board[from.1][from.0]
    board[from.1][from.0] = empty()
    board[to.1][to.0] = pieceToMove
board[move: (4,5)] = (2,3)
board.displayBoard()

Sự thay đổi của board from

-r-r-r-r
r-r-r-r-
-r-r-r-r
--------
---r----
b---b-b-
-b-b-b-b
b-b-b-b-

To

-r-r-r-r
r-r-r-r-
-r-r-r-r
--b-----
--------
b-----b-
-b-b-b-b
b-b-b-b-

Các mảng màu đen giờ sẽ di chuyển sang các mảnh màu đỏ.Bây giờ kiểm tra kiểm tra lỗi bằng cách cố gắng di chuyển khác. Nên nhớ các lệnh di chuyển ra ngoài mảng sẽ đc thông báo lỗi

board[move: (1,6)] = (2,5) //legal move
board.displayBoard()

board[move: (1,6)] = (1,5) //No piece to move since
board.displayBoard() //you just moved the piece at (1,6)

board[move: (7,2)] = (6,1) //(6,1) is already occupied
board.displayBoard()

board[move: (7,2)] = (5,4) //Illegal jump - no piece to jump
board.displayBoard()

board[move: (7,2)] = (8,1) //Range Error
board.displayBoard()

board[move: (0,7)] = (1,6) //legal move
board.displayBoard()

Cuối cùng là source code đầy đủ tại link sau

http://cdn5.raywenderlich.com/wp-content/uploads/2014/10/FinalSubscriptPlayground.playground1.zip

All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.