Hướng dẫn phong cách python enum

Một phép liệt kê xác định một kiểu chung cho một nhóm các giá trị có liên quan và cho phép bạn làm việc với các giá trị đó theo cách an toàn kiểu trong mã của bạn

Nếu bạn đã quen thuộc với C, bạn sẽ biết rằng kiểu liệt kê trong C gán các tên liên quan cho một tập hợp các giá trị số nguyên. Phép liệt kê trong Swift linh hoạt hơn nhiều và không phải cung cấp giá trị cho từng trường hợp của phép liệt kê. Nếu một giá trị (được gọi là giá trị thô) được cung cấp cho từng trường hợp liệt kê, thì giá trị đó có thể là một chuỗi, một ký tự hoặc một giá trị thuộc bất kỳ kiểu số nguyên hoặc dấu phẩy động nào

Ngoài ra, các trường hợp liệt kê có thể chỉ định các giá trị liên quan thuộc bất kỳ loại nào sẽ được lưu trữ cùng với từng giá trị trường hợp khác nhau, giống như các liên kết hoặc biến thể thực hiện trong các ngôn ngữ khác. Bạn có thể xác định một tập hợp phổ biến các trường hợp có liên quan như một phần của một phép liệt kê, mỗi trường hợp có một tập hợp giá trị khác nhau của các loại phù hợp được liên kết với nó

Bảng liệt kê trong Swift là loại hạng nhất theo cách riêng của chúng. Chúng áp dụng nhiều tính năng theo truyền thống chỉ được hỗ trợ bởi các lớp, chẳng hạn như các thuộc tính được tính toán để cung cấp thông tin bổ sung về giá trị hiện tại của phép liệt kê và các phương thức thể hiện để cung cấp chức năng liên quan đến các giá trị mà phép liệt kê đại diện. Việc liệt kê cũng có thể xác định các trình khởi tạo để cung cấp giá trị trường hợp ban đầu;

Để biết thêm về các khả năng này, hãy xem Thuộc tính , Phương thức, Initialization, Extensions, and Protocols.

Cú pháp liệt kê¶

Bạn giới thiệu kiểu liệt kê với từ khóa enum và đặt toàn bộ định nghĩa của chúng trong một cặp dấu ngoặc nhọn

  1. enum SomeEnumeration {
  2. // định nghĩa liệt kê ở đây
  3. }

Đây là một ví dụ cho bốn điểm chính của la bàn

  1. enum Compasspoint {
  2. trường hợp bắc
  3. trường hợp nam
  4. trường hợp đông
  5. trường hợp tây
  6. }

Các giá trị được xác định trong một phép liệt kê (chẳng hạn như north, south, eastwest) là các trường hợp liệt kê của nó. Bạn sử dụng từ khóa case để giới thiệu các trường hợp liệt kê mới

Ghi chú

Các trường hợp liệt kê Swift không có giá trị số nguyên được đặt theo mặc định, không giống như các ngôn ngữ như C và Objective-C. Trong ví dụ về CompassPoint ở trên, north, south, eastwest không hoàn toàn bằng với north1, north2, north3 và north4. Thay vào đó, các trường hợp liệt kê khác nhau là các giá trị theo đúng nghĩa của chúng, với một loại được xác định rõ ràng là CompassPoint

Nhiều trường hợp có thể xuất hiện trên một dòng, được phân tách bằng dấu phẩy

  1. enum Hành tinh {
  2. trường hợp thủy ngân , sao kim , earth, mars, jupiter, saturn, uranus, neptune
  3. }

Mỗi định nghĩa liệt kê xác định một loại mới. Giống như các loại khác trong Swift, tên của chúng (chẳng hạn như CompassPointnorth7) bắt đầu bằng một chữ in hoa. Đặt tên cho các kiểu liệt kê là số ít thay vì số nhiều để chúng đọc là hiển nhiên

  1. var directionToHead = CompassPoint . tây

Loại north8 được suy ra khi nó được khởi tạo với một trong các giá trị có thể có của CompassPoint. Khi north8 được khai báo là CompassPoint, bạn có thể đặt nó thành một giá trị CompassPoint khác bằng cách sử dụng cú pháp dấu chấm ngắn hơn

  1. directionToHead =. đông

Loại north8 đã được biết và vì vậy bạn có thể loại bỏ loại khi đặt giá trị của nó. Điều này làm cho mã dễ đọc hơn khi làm việc với các giá trị liệt kê được nhập rõ ràng

Kết hợp các giá trị liệt kê với câu lệnh chuyển đổi¶

Bạn có thể khớp các giá trị liệt kê riêng lẻ với câu lệnh south4

  1. directionToHead =. nam
  2. chuyển directionToHead {
  3. trường hợp . bắc .
  4. print ( "Rất nhiều hành tinh có hướng bắc" )
  5. trường hợp . nam .
  6. print ( "Coi chừng chim cánh cụt" )
  7. trường hợp . phía đông .
  8. print ( "Nơi mặt trời mọc" )
  9. trường hợp . tây .
  10. print ( "Nơi bầu trời xanh" )
  11. }
  12. // In ra "Coi chừng chim cánh cụt"

Bạn có thể đọc mã này như

“Hãy xem xét giá trị của north8. Trong trường hợp nó bằng south6, in ra south7. Trong trường hợp nó bằng south8, in ra south9. ”

…và như thế

Như được mô tả trong Luồng điều khiển , một câu lệnh south4 phải đầy đủ khi xem xét các trường hợp của bảng liệt kê. Nếu case cho east2 bị bỏ qua, mã này không được biên dịch vì nó không xem xét danh sách đầy đủ của các trường hợp CompassPoint. Yêu cầu tính toàn diện đảm bảo rằng các trường hợp liệt kê không vô tình bị bỏ qua.

Khi không thích hợp để cung cấp một case cho mọi trường hợp liệt kê, bạn có thể cung cấp một trường hợp east5 để bao gồm bất kỳ trường hợp nào không được giải quyết rõ ràng

  1. let somePlanet = Hành tinh . trái đất
  2. chuyển somePlanet {
  3. trường hợp . trái đất .
  4. in ( "Hầu như vô hại" )
  5. mặc định .
  6. print ( "Không phải là nơi an toàn cho con người" )
  7. }
  8. // In ra "Hầu như vô hại"

Lặp lại các trường hợp liệt kê¶

Đối với một số kiểu liệt kê, sẽ rất hữu ích khi có một tập hợp tất cả các trường hợp của kiểu liệt kê đó. Bạn kích hoạt tính năng này bằng cách viết east6 sau tên của bảng liệt kê. Swift hiển thị một tập hợp tất cả các trường hợp dưới dạng thuộc tính east7 của kiểu liệt kê. Đây là một ví dụ

  1. enum Đồ uống . CaseIterable {
  2. hộp cà phê , trà , juice
  3. }
  4. let numberOfChoices = Đồ uống . tất cả các trường hợp . đếm
  5. in ( " \ (numberOfChoices) beverages available")
  6. // In ra "3 loại đồ uống có sẵn"

Trong ví dụ trên, bạn viết east8 để truy cập một bộ sưu tập chứa tất cả các trường hợp của phép liệt kê east9. Bạn có thể sử dụng east7 giống như bất kỳ bộ sưu tập nào khác—các phần tử của bộ sưu tập là các thể hiện của kiểu liệt kê, vì vậy trong trường hợp này, chúng là các giá trị east9. Ví dụ trên đếm xem có bao nhiêu trường hợp và ví dụ bên dưới sử dụng vòng lặp west2-west3 để lặp qua tất cả các trường hợp

  1. cho đồ uống trong Đồ uống . tất cả các trường hợp {
  2. in ( đồ uống )
  3. }
  4. // cà phê
  5. // trà
  6. // Nước ép

Cú pháp được sử dụng trong các ví dụ trên đánh dấu phép liệt kê là tuân theo giao thức west4. Để biết thông tin về các giao thức, hãy xem Giao thức .

Giá trị liên kết¶

Các ví dụ trong phần trước cho thấy cách các trường hợp liệt kê là một giá trị được xác định (và nhập) theo đúng nghĩa của chúng. Bạn có thể đặt hằng số hoặc biến thành west5 và kiểm tra giá trị này sau. Tuy nhiên, đôi khi rất hữu ích khi có thể lưu trữ các giá trị của các loại khác cùng với các giá trị trường hợp này. Thông tin bổ sung này được gọi là giá trị liên kết và nó thay đổi mỗi khi bạn sử dụng trường hợp đó làm giá trị trong mã của mình

Bạn có thể xác định kiểu liệt kê Swift để lưu trữ các giá trị được liên kết của bất kỳ loại cụ thể nào và các loại giá trị có thể khác nhau đối với từng trường hợp liệt kê nếu cần. Các phép liệt kê tương tự như thế này được gọi là các hiệp hội phân biệt đối xử, các hiệp hội được gắn thẻ hoặc các biến thể trong các ngôn ngữ lập trình khác

Ví dụ: giả sử hệ thống theo dõi hàng tồn kho cần theo dõi sản phẩm theo hai loại mã vạch khác nhau. Một số sản phẩm được dán nhãn mã vạch 1D ở định dạng UPC, sử dụng các số từ north1 đến west7. Mỗi mã vạch có một chữ số hệ thống số, theo sau là năm chữ số mã nhà sản xuất và năm chữ số mã sản phẩm. Theo sau chúng là một chữ số kiểm tra để xác minh rằng mã đã được quét chính xác

Hướng dẫn phong cách python enum

Các sản phẩm khác được dán nhãn bằng mã vạch 2D ở định dạng mã QR, có thể sử dụng bất kỳ ký tự ISO 8859-1 nào và có thể mã hóa chuỗi dài tối đa 2.953 ký tự

Hướng dẫn phong cách python enum

Thật thuận tiện cho hệ thống theo dõi hàng tồn kho để lưu trữ mã vạch UPC dưới dạng một bộ gồm bốn số nguyên và mã vạch mã QR dưới dạng một chuỗi có độ dài bất kỳ

Trong Swift, một bảng liệt kê để xác định mã vạch sản phẩm thuộc một trong hai loại có thể trông như thế này

  1. enum Mã vạch {
  2. trường hợp upc ( Int , Int, Int, Int)
  3. trường hợp qrCode ( Chuỗi )
  4. }

Điều này có thể được đọc như

“Xác định một kiểu liệt kê có tên là west8, có thể lấy giá trị của west9 với giá trị được liên kết của loại (case0, case0, case0, case0) hoặc giá trị của case4 với giá trị được liên kết của loại case5. ”

Định nghĩa này không cung cấp bất kỳ giá trị thực tế nào của case0 hoặc case5—nó chỉ xác định loại giá trị được liên kết mà hằng và biến west8 có thể lưu trữ khi chúng bằng với case9 hoặc CompassPoint0

Sau đó, bạn có thể tạo mã vạch mới bằng một trong hai loại

  1. var productBarcode = Barcode . upc ( 8 , 85909, 51226, 3)

Ví dụ này tạo một biến mới có tên là CompassPoint1 và gán cho nó một giá trị là case9 với một giá trị bộ dữ liệu được liên kết là CompassPoint3

Bạn có thể gán cho cùng một sản phẩm một loại mã vạch khác

  1. mã vạch sản phẩm =. qrCode ( "ABCDEFGHIJKLMNOP" )

Tại thời điểm này, case9 ban đầu và các giá trị số nguyên của nó được thay thế bằng CompassPoint0 mới và giá trị chuỗi của nó. Các hằng số và biến loại west8 có thể lưu trữ một CompassPoint7 hoặc một CompassPoint8 (cùng với các giá trị được liên kết của chúng), nhưng chúng chỉ có thể lưu trữ một trong số chúng tại bất kỳ thời điểm nào

Bạn có thể kiểm tra các loại mã vạch khác nhau bằng cách sử dụng câu lệnh chuyển đổi, tương tự như ví dụ trong Kết hợp giá trị liệt kê với câu lệnh chuyển đổi . Tuy nhiên, lần này, các giá trị liên quan được trích xuất như một phần của câu lệnh chuyển đổi. Bạn trích xuất từng giá trị được liên kết dưới dạng một hằng số (với tiền tố CompassPoint9) hoặc một biến (với tiền tố north0) để sử dụng trong phần thân của trường hợp south4.

  1. chuyển đổi mã vạch sản phẩm {
  2. trường hợp . upc ( let numberSystem , . let manufacturer, let product, let check):
  3. in ( "UPC. \ (hệ thống số), . "\(manufacturer), \(product), \(check)." )
  4. trường hợp . qrCode ( cho phép Mã sản phẩm ).
  5. in ( "Mã QR. \ (Mã sản phẩm). " )
  6. }
  7. // In "mã QR. ABCDEFGHIJKLMNOP. "

Nếu tất cả các giá trị được liên kết cho một trường hợp liệt kê được trích xuất dưới dạng hằng số hoặc nếu tất cả được trích xuất dưới dạng biến, bạn có thể đặt một chú thích north0 hoặc CompassPoint9 trước tên trường hợp để cho ngắn gọn

  1. chuyển đổi mã vạch sản phẩm {
  2. trường hợp hãy . upc ( sốHệ thống , nhà sản xuất . , product, check):
  3. in ( "UPC. \ (hệ thống số), . "\(manufacturer), \(product), \(check)." )
  4. trường hợp hãy . qrCode ( Mã sản phẩm ).
  5. in ( "Mã QR. \ (Mã sản phẩm). " )
  6. }
  7. // In "mã QR. ABCDEFGHIJKLMNOP. "

Giá trị thô¶

Ví dụ về mã vạch trong Giá trị được liên kết cho biết cách các trường hợp liệt kê có thể tuyên bố rằng chúng lưu trữ các giá trị được liên kết thuộc các loại khác nhau. Để thay thế cho các giá trị được liên kết, các trường hợp liệt kê có thể được điền sẵn các giá trị mặc định (được gọi là giá trị thô), tất cả đều thuộc cùng một loại.

Đây là một ví dụ lưu trữ các giá trị ASCII thô cùng với các trường hợp liệt kê được đặt tên

  1. enum ASCIIControl Character . Nhân vật {
  2. trường hợp tab = "\t"
  3. case lineFeed = "\n"
  4. case carriageReturn = "\r"
  5. }

Ở đây, các giá trị thô cho một phép liệt kê có tên là north4 được định nghĩa là thuộc loại north5 và được đặt thành một số ký tự điều khiển ASCII phổ biến hơn. Các giá trị của north5 được mô tả trong Chuỗi và Ký tự .

Giá trị thô có thể là chuỗi, ký tự hoặc bất kỳ loại số nguyên hoặc số dấu phẩy động nào. Mỗi giá trị thô phải là duy nhất trong khai báo liệt kê của nó

Ghi chú

Giá trị thô không giống với giá trị được liên kết. Các giá trị thô được đặt thành các giá trị được điền trước khi bạn xác định phép liệt kê trong mã của mình lần đầu tiên, chẳng hạn như ba mã ASCII ở trên. Giá trị thô cho một trường hợp liệt kê cụ thể luôn giống nhau. Các giá trị được liên kết được đặt khi bạn tạo một hằng số hoặc biến mới dựa trên một trong các trường hợp của phép liệt kê và có thể khác nhau mỗi khi bạn làm như vậy

Giá trị thô được gán ngầm¶

Khi bạn đang làm việc với các kiểu liệt kê lưu trữ các giá trị nguyên dạng chuỗi hoặc số nguyên, bạn không cần phải chỉ định một cách rõ ràng giá trị nguyên cho từng trường hợp. Khi bạn không làm như vậy, Swift sẽ tự động gán các giá trị cho bạn

Ví dụ: khi số nguyên được sử dụng cho giá trị thô, giá trị ẩn cho mỗi trường hợp nhiều hơn trường hợp trước một. Nếu trường hợp đầu tiên không có giá trị được đặt, giá trị của nó là north1

Phép liệt kê bên dưới là sự sàng lọc của phép liệt kê north7 trước đó, với các giá trị nguyên nguyên để biểu thị thứ tự của mỗi hành tinh tính từ mặt trời

  1. enum Hành tinh . Int {
  2. trường hợp thủy ngân = 1 , venus, earth, mars, jupiter, saturn, uranus, neptune
  3. }

Trong ví dụ trên, north9 có giá trị thô rõ ràng là north2, south1 có giá trị thô tiềm ẩn là north3, v.v.

Khi các chuỗi được sử dụng cho các giá trị thô, giá trị ẩn cho từng trường hợp là văn bản tên của trường hợp đó

Phép liệt kê bên dưới là sự sàng lọc của phép liệt kê CompassPoint trước đó, với các giá trị thô của chuỗi để biểu thị tên của từng hướng

  1. enum Compasspoint . Chuỗi {
  2. trường hợp bắc , nam , east, west
  3. }

Trong ví dụ trên, south4 có giá trị thô tiềm ẩn là south5, v.v.

Bạn truy cập giá trị thô của trường hợp liệt kê với thuộc tính south6 của nó

  1. let Trật tự trái đất = Hành tinh . trái đất . rawValue
  2. // EarthsOrder là 3
  3. let Hướng hoàng hôn = La bàn . tây . rawValue
  4. // hướng hoàng hôn là "tây"

Khởi tạo từ Giá trị thô¶

Nếu bạn xác định một phép liệt kê với loại giá trị thô, thì phép liệt kê sẽ tự động nhận một trình khởi tạo lấy giá trị của loại giá trị thô (dưới dạng tham số có tên là south6) và trả về trường hợp liệt kê hoặc south8. Bạn có thể sử dụng trình khởi tạo này để thử tạo một thể hiện mới của phép liệt kê

Ví dụ này xác định Sao Thiên Vương từ giá trị thô của nó là south9

  1. let possiblePlanet = Hành tinh ( rawValue: 7 )
  2. // có thểPlanet thuộc loại Planet? . Sao Thiên Vương

Tuy nhiên, không phải tất cả các giá trị case0 có thể sẽ tìm thấy một hành tinh phù hợp. Do đó, trình khởi tạo giá trị thô luôn trả về một trường hợp liệt kê tùy chọn. Trong ví dụ trên, east1 thuộc loại east2 hoặc “tùy chọn north7. ”

Ghi chú

Trình khởi tạo giá trị thô là trình khởi tạo có thể gặp lỗi, bởi vì không phải mọi giá trị thô sẽ trả về trường hợp liệt kê. Để biết thêm thông tin, hãy xem Trình khởi chạy không thành công .

Nếu bạn cố gắng tìm một hành tinh có vị trí là east4, thì giá trị tùy chọn north7 được trả về bởi bộ khởi tạo giá trị thô sẽ là south8

  1. let positionToFind = 11
  2. if let somePlanet = Hành tinh(rawValue: positionToFind ) {
  3. chuyển somePlanet {
  4. trường hợp . trái đất .
  5. in ( "Hầu như vô hại" )
  6. mặc định .
  7. print ( "Không phải là nơi an toàn cho con người" )
  8. }
  9. } khác {
  10. print ( "Không có hành tinh nào ở vị trí \ (positionToFind)")
  11. }
  12. // In ra "Không có hành tinh nào ở vị trí 11"

Ví dụ này sử dụng liên kết tùy chọn để thử truy cập một hành tinh có giá trị thô là east4. Câu lệnh east8 tạo một north7 tùy chọn và đặt north00 thành giá trị của north7 tùy chọn đó nếu nó có thể được truy xuất. Trong trường hợp này, không thể truy xuất một hành tinh có vị trí là east4 và do đó, nhánh north03 được thực thi thay thế

Phép liệt kê đệ quy¶

Phép liệt kê đệ quy là phép liệt kê có một thể hiện khác của phép liệt kê làm giá trị liên quan cho một hoặc nhiều trường hợp liệt kê. Bạn chỉ ra rằng trường hợp liệt kê là đệ quy bằng cách viết north04 trước nó, điều này báo cho trình biên dịch chèn lớp điều hướng cần thiết

Ví dụ, đây là một kiểu liệt kê lưu trữ các biểu thức số học đơn giản

  1. enum Biểu thức số học {
  2. trường hợp số ( Int )
  3. gián tiếp trường hợp phép cộng ( Biểu thức số học, ArithmeticExpression)
  4. gián tiếp trường hợp phép nhân ( Biểu thức số học, ArithmeticExpression)
  5. }

Bạn cũng có thể viết north04 trước khi bắt đầu phép liệt kê để bật tính năng gián tiếp cho tất cả các trường hợp của phép liệt kê có giá trị liên quan

  1. gián tiếp enum Biểu thức số học {
  2. trường hợp số ( Int )
  3. trường hợp phép cộng ( Biểu thức số học , ArithmeticExpression)
  4. trường hợp phép nhân ( Biểu thức số học , ArithmeticExpression)
  5. }

Kiểu liệt kê này có thể lưu trữ ba loại biểu thức số học. một số đơn giản, phép cộng hai biểu thức và phép nhân hai biểu thức. Các trường hợp north06 và north07 có các giá trị được liên kết cũng là các biểu thức số học—các giá trị được liên kết này giúp có thể lồng các biểu thức. Ví dụ, biểu thức north08 có một số ở bên phải của phép nhân và một biểu thức khác ở bên trái của phép nhân. Vì dữ liệu được lồng vào nhau nên phép liệt kê được sử dụng để lưu trữ dữ liệu cũng cần hỗ trợ lồng nhau—điều này có nghĩa là phép liệt kê cần phải được đệ quy. Mã bên dưới hiển thị phép liệt kê đệ quy north09 được tạo cho north08

  1. hãy năm = Biểu thức số học . số ( 5 )
  2. hãy bốn = Biểu thức số học . số ( 4 )
  3. hãy tổng = Biểu thức số học . bổ sung ( năm , bốn)
  4. let product = Biểu thức số học . phép nhân ( tổng , Biểu thức số học . .số ( 2 ))

Hàm đệ quy là một cách đơn giản để làm việc với dữ liệu có cấu trúc đệ quy. Ví dụ: đây là một hàm đánh giá một biểu thức số học

  1. hàm đánh giá ( _ biểu thức: Biểu thức số học ) -> Int {
  2. chuyển biểu thức {
  3. trường hợp hãy . số ( giá trị ).
  4. trả về giá trị
  5. trường hợp hãy . bổ sung ( trái , phải . ):
  6. return đánh giá ( left ) + evaluate(right)
  7. trường hợp hãy . phép nhân ( trái , phải . ):
  8. return đánh giá ( left ) * evaluate(right)
  9. }
  10. }
  11. in ( đánh giá ( sản phẩm))
  12. // In ra "18"

Hàm này đánh giá một số đơn giản bằng cách trả về giá trị được liên kết. Nó đánh giá một phép cộng hoặc phép nhân bằng cách đánh giá biểu thức ở vế trái, đánh giá biểu thức ở vế phải, rồi cộng hoặc nhân chúng