Tạo triển khai cho các loại chung trong Go

Go có các kiểu tĩnh, mạnh và nó không hỗ trợ các kiểu chung, vậy làm thế nào chúng ta có thể xác định các cấu trúc dữ liệu mục đích chung và các thuật toán có thể áp dụng cho nhiều hơn một kiểu?interface{}s không phải là giải pháp, vì chúng yêu cầu đúc và chúng tôi mất rất nhiều lợi thế của việc gõ tĩnh mạnh mẽ.Giải pháp là tạo mã, vì nó cho phép kiểm tra thời gian biên dịch và độ an toàn cũng như hiệu suất cao hơn.

Tôi không tìm thấy thông tin này dễ dàng truy cập khi tìm kiếm các chủ đề tạo mã, nhưng tôi đã vấp phải các trường hợp và tình huống sử dụng phức tạp hơn, vì vậy đây là nó, được giải thích bằng tiếng Anh đơn giản.

Vấn đề:

tôi muốntriển khai cấu trúc dữ liệu(điều tương tự cũng áp dụng cho một thuật toán)theo cách chung nhất có thểvới cờ vây vàtạo các triển khai loại cụ thể mà tôi có thể dễ dàng sử dụng lại.

Giải pháp:

Sử dụnggenny, điều này không thể đơn giản hơn:

  1. nhập khẩugenny/generic
  2. xác định một hoặc nhiều loại dưới dạnggeneric.Type, ví dụ: gọi họItemhoặc làType
  3. sử dụng những loại này trong mã của bạn
  4. chạygennyđể tạo triển khai loại cụ thể

Thí dụ

Kiểm tra ví dụ đơn giản này, đây là phiên bản rút gọn củaĐặt triển khai Cấu trúc dữ liệu trong Go:

// Package set creates a ItemSet data structure for the Item type
package set

import github.com/cheekybits/genny/generic

// Item the type of the Set type Item generic.Type

// ItemSet the set of Items type ItemSet struct { items map[Item]bool }

// Add adds a new element to the Set. Returns the Set. func (s ItemSet) Add(t Item) ItemSet { if s.items == nil { s.items = make(map[Item]bool) } _, ok := s.items[t] if !ok { s.items[t] = true } return s }

// Clear removes all elements from the Set func (s ItemSet) Clear() { (s).items = make(map[Item]bool) }

Bằng cách chạy

genny -in set.go -out gen-set.go gen "Item=string,int"

trong dòng lệnh ngay bây giờ, nếu tệp được gọiset.gonó sẽ tạo ra mộtgen-set.gotrong đó có những thứ sau:

// This file was automatically generated by genny.
// Any changes will be lost if this file is regenerated.
// see https://github.com/cheekybits/genny

// Package Set creates a StringSet data structure for the string type
package set

// StringSet the set of Strings type StringSet struct { items map[string]bool }

// Add adds a new element to the Set. Returns the Set. func (s StringSet) Add(t string) StringSet { s.items[t] = true return s }

// Clear removes all elements from the Set func (s StringSet) Clear() { (s).items = make(map[string]bool) }

// Delete removes the string from the Set and returns Has(string) func (s StringSet) Delete(item string) bool { ret := (s).Has(item) if ret { delete((*s).items, item) } return ret }

// Has returns true if the Set contains the string func (s StringSet) Has(item string) bool { return (s).items[item] }

// Strings returns the string(s) stored func (s *StringSet) Strings() []string { items := []string{} for i := range s.items { items = append(items, i) } return items }

// Package Set creates a IntSet data structure for the int type // IntSet the set of Ints type IntSet struct { items map[int]bool }

// Add adds a new element to the Set. Returns the Set. func (s IntSet) Add(t int) IntSet { s.items[t] = true return s }

// Clear removes all elements from the Set func (s IntSet) Clear() { (s).items = make(map[int]bool) }

// Delete removes the int from the Set and returns Has(int) func (s IntSet) Delete(item int) bool { ret := (s).Has(item) if ret { delete((*s).items, item) } return ret }

// Has returns true if the Set contains the int func (s IntSet) Has(item int) bool { return (s).items[item] }

// Ints returns the int(s) stored func (s *IntSet) Ints() []int { items := []int{} for i := range s.items { items = append(items, i) } return items }

Bạn có thể thấy,gennyđã tạo raStringSetIntSetcấu trúc từItemSet**, vì đã nói với nóItem=string,int.

Nó cũng tạo ra các phương thức cấu trúctạo ra các triển khai Set của chúng tôi.

Nếu chúng ta muốn thêm một tập hợp các loại khác vào ứng dụng của mình, chúng ta có thể chỉnh sửa lệnh và chạy lại.

Sử dụnggo generate

Bạn cũng có thể tự động hóa việc này bằng cách thêm nhận xét ở đầu tệp, bên dưới tài liệu gói, như:

// go:generate genny -in=$GOFILE -out=gen-$GOFILE gen "Item=string,int"

Điều này nói vớigo generate, thông qua sự giúp đỡ củagenny, để tạo một phiên bản dịchItemđếnstringvà phiên bản thứ hai đang dịchitemđếnint.

Chạy

go generate

để có đượcgen-set.gotệp, với chuỗi và tập hợp int.

Thử nghiệm

Quan trọng: triển khai chung của cấu trúc dữ liệu ở trênbiên dịch và có thể được kiểm tradễ dàng như một triển khai cụ thể được tạo ra bởigo generate, vì vậy chúng tôi có thể chạy các thử nghiệm đối với việc triển khai chung của chúng tôi, cũng như các thử nghiệm dành riêng cho từng loại.


Các hướng dẫn về go khác: