در این آموزش، ابتدا دو تابع غیرژنریک (Non-generic) ساده تعریف میکنید و سپس همان منطق را در قالب یک تابع ژنریک منفرد پیادهسازی مینمایید.
مراحل این آموزش عبارتند از:
-
ایجاد پوشه برای کدها
-
افزودن توابع غیرژنریک
-
افزودن یک تابع ژنریک برای پشتیبانی از چندین تیپ (Type)
-
حذف آرگومانهای تیپ هنگام فراخوانی تابع ژنریک
-
تعریف یک محدودکننده تیپ (Type Constraint)
پیشنیازها
-
Go: توصیه میشود از آخرین نسخه Go برای انجام این آموزش استفاده کنید.
-
ویرایشگر کد: هر ویرایشگر متنی (مانند VS Code) قابل استفاده است.
-
ترمینال (Command Prompt): ترمینال در لینوکس، مک یا PowerShell/cmd در ویندوز.
۱. ایجاد پوشه برای کدها
ترمینال را باز کرده و به مسیر Home بروید:
در لینوکس یا مک:
$ cd
در ویندوز:
C:\> cd %HOMEPATH%
یک پوشه به نام generics بسازید و وارد آن شوید:
$ mkdir generics
$ cd generics
یک ماژول Go برای مدیریت پروژه ایجاد کنید:
$ go mod init example/generics
go: creating new go.mod: module example/generics
۲. افزودن توابع غیرژنریک
در این مرحله دو تابع مینویسید که هرکدام مقادیر یک Map را جمع زده و مجموع را برمیگردانند. علت تعریف دو تابع این است که با دو نوع Map متفاوت کار میکنید: یکی با مقادیر int64 و دیگری با مقادیر float64.
نوشتن کد
فایلی به نام main.go در پوشه پروژه ایجاد کنید.
در بالای فایل عبارت زیر را قرار دهید:
package main
توابع زیر را زیر عبارت فوق اضافه کنید:
// SumInts مقادیر درون m را جمع میزند.
func SumInts(m map[string]int64) int64 {
var s int64
for _, v := range m {
s += v
}
return s
}
// SumFloats مقادیر درون m را جمع میزند.
func SumFloats(m map[string]float64) float64 {
var s float64
for _, v := range m {
s += v
}
return s
}
حالا تابع main و دستور import را اضافه کنید:
package main
import "fmt"
func main() {
// مقداردهی اولیه یک map برای مقادیر صحیح (int)
ints := map[string]int64{
"first": 34,
"second": 12,
}
// مقداردهی اولیه یک map برای مقادیر اعشاری (float)
floats := map[string]float64{
"first": 35.98,
"second": 26.99,
}
fmt.Printf("Non-Generic Sums: %v and %v\n",
SumInts(ints),
SumFloats(floats))
}
فایل را ذخیره کرده و اجرا کنید:
$ go run .
Non-Generic Sums: 46 and 62.97
۳. افزودن یک تابع ژنریک برای پشتیبانی از چندین تیپ
در این بخش، یک تابع ژنریک جایگزین دو تابع قبلی میکنیم تا بتواند Mapهایی با مقادیر int64 یا float64 را دریافت کند.
در Go، پارامترهای تیپ (Type Parameters) داخل قلاب ([]) قرار میگیرند و برای آنها یک محدودکننده (Type Constraint) مشخص میشود.
نوشتن کد
تابع ژنریک زیر را زیر توابع قبلی اضافه کنید:
// SumIntsOrFloats مقادیر map را جمع میزند.
// این تابع از هر دو نوع int64 و float64 برای مقادیر map پشتیبانی میکند.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
var s V
for _, v := range m {
s += v
}
return s
}
توضیحات:
-
K comparable: کلیدهای Map باید قابل مقایسه (با==و!=) باشند. محدودکننده pre-declaredشدهcomparableدر Go برای این کار استفاده میشود. -
V int64 | float64: محدودکنندهVیک اجتماع (Union) از دو تیپint64وfloat64است (با استفاده از کاراکتر|).
حالا خطوط زیر را به انتهای تابع main اضافه کنید:
fmt.Printf("Generic Sums: %v and %v\n",
SumIntsOrFloats[string, int64](ints),
SumIntsOrFloats[string, float64](floats))
برنامه را اجرا کنید:
$ go run .
Non-Generic Sums: 46 and 62.97
Generic Sums: 46 and 62.97
۴. حذف آرگومانهای تیپ هنگام فراخوانی تابع ژنریک
کامپایلر Go در اکثر مواقع میتواند تیپها را از روی پارامترهای ورودی استنتاج (Infer) کند؛ بنابراین نیازی به نوشتن صریح [string, int64] در زمان فراخوانی نیست.
نوشتن کد
کد زیر را به انتهای تابع main اضافه کنید:
fmt.Printf("Generic Sums, type parameters inferred: %v and %v\n",
SumIntsOrFloats(ints),
SumIntsOrFloats(floats))
کد را اجرا کنید:
$ go run .
Non-Generic Sums: 46 and 62.97
Generic Sums: 46 and 62.97
Generic Sums, type parameters inferred: 46 and 62.97
۵. تعریف یک محدودکننده تیپ (Type Constraint)
برای تمیزتر شدن کد و امکان استفاده مجدد، میتوانید محدودکننده اجتماع تیپها را در قالب یک اینترفیس (Interface) تعریف کنید.
نوشتن کد
بالای تابع main (زیر دستورات import)، اینترفیس زیر را تعریف کنید:
type Number interface {
int64 | float64
}
سپس تابع ژنریک زیر را که از این اینترفیس استفاده میکند اضافه نمایید:
// SumNumbers مقادیر map را جمع میزند.
// از اعداد صحیح و اعشاری پشتیبانی میکند.
func SumNumbers[K comparable, V Number](m map[K]V) V {
var s V
for _, v := range m {
s += v
}
return s
}
در تابع main کد زیر را برای فراخوانی اضافه کنید:
fmt.Printf("Generic Sums with Constraint: %v and %v\n",
SumNumbers(ints),
SumNumbers(floats))
برنامه را اجرا کنید:
$ go run .
Non-Generic Sums: 46 and 62.97
Generic Sums: 46 and 62.97
Generic Sums, type parameters inferred: 46 and 62.97
Generic Sums with Constraint: 46 and 62.97
کد کامل پروژه (main.go)
package main
import "fmt"
type Number interface {
int64 | float64
}
func main() {
// مقداردهی اولیه یک map برای مقادیر صحیح
ints := map[string]int64{
"first": 34,
"second": 12,
}
// مقداردهی اولیه یک map برای مقادیر اعشاری
floats := map[string]float64{
"first": 35.98,
"second": 26.99,
}
fmt.Printf("Non-Generic Sums: %v and %v\n",
SumInts(ints),
SumFloats(floats))
fmt.Printf("Generic Sums: %v and %v\n",
SumIntsOrFloats[string, int64](ints),
SumIntsOrFloats[string, float64](floats))
fmt.Printf("Generic Sums, type parameters inferred: %v and %v\n",
SumIntsOrFloats(ints),
SumIntsOrFloats(floats))
fmt.Printf("Generic Sums with Constraint: %v and %v\n",
SumNumbers(ints),
SumNumbers(floats))
}
// SumInts مقادیر m را جمع میزند.
func SumInts(m map[string]int64) int64 {
var s int64
for _, v := range m {
s += v
}
return s
}
// SumFloats مقادیر m را جمع میزند.
func SumFloats(m map[string]float64) float64 {
var s float64
for _, v := range m {
s += v
}
return s
}
// SumIntsOrFloats مقادیر map را جمع میزند و از int64 و float64 پشتیبانی میکند.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
var s V
for _, v := range m {
s += v
}
return s
}
// SumNumbers مقادیر map را با استفاده از محدودکننده Number جمع میزند.
func SumNumbers[K comparable, V Number](m map[K]V) V {
var s V
for _, v := range m {
s += v
}
return s
}
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: منبع مستندات