sort.Interface
חלק מהיחידה תכנות מונחה עצמים במסלול ה-GO של Coddy. שיעור 74 מתוך 107.
החבילה sort מספקת דוגמה מצוינת נוספת לתכנון המבוסס על ממשקים. כדי למיין אוסף מותאם אישית, הטיפוס שלך חייב לממש את sort.Interface:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}שלוש המתודות האלה מספקות לאלגוריתם המיון את כל מה שהוא צריך: את אורך האוסף, דרך להשוות בין איברים ודרך להחליף ביניהם. כך אפשר להפוך פרוסה של מבנים מותאמים אישית לניתנת למיון:
type Person struct {
Name string
Age int
}
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func main() {
people := []Person{
{"Alice", 30},
{"Bob", 25},
{"Carol", 35},
}
sort.Sort(ByAge(people))
fmt.Println(people)
// [{Bob 25} {Alice 30} {Carol 35}]
}התובנה המרכזית היא ליצור טיפוס בעל שם (ByAge) המבוסס על הפרוסה שלך. כך אפשר להגדיר התנהגויות מיון שונות לאותם נתונים. אפשר ליצור ByName עם מימוש שונה של Less כדי למיין לפי סדר אלפביתי במקום זאת.
ברגע שהטיפוס שלך מממש את sort.Interface, הוא עובד אוטומטית עם sort.Sort(), sort.Reverse() ועם sort.IsSorted().
אתגר
קלבואו נבנה מערכת מלאי מוצרים הניתנת למיון, שמדגימה את העוצמה של sort.Interface של Go! תיצרו טיפוס Product ותממשו כמה אסטרטגיות מיון, כך שאפשר יהיה למיין את אותו אוסף בדרכים שונות.
תארגנו את הקוד בשני קבצים:
product.go: הגדירו את טיפוס המוצר ואת מימושי המיון.צרו מבנה
Productעם שלושה שדות:Name(string),Price(float64) ו-Quantity(int).צרו שני טיפוסים בעלי שם המבוססים על
[]Product:ByPrice- למיון מוצרים לפי מחיר בסדר עולהByQuantity- למיון מוצרים לפי כמות בסדר יורד (הכמות הגדולה ביותר תחילה)
כל טיפוס צריך לממש את שלוש המתודות הנדרשות על ידי
sort.Interface:Len(),Less(i, j int)ו-Swap(i, j int). המתודהLessקובעת את סדר המיון עבור כל טיפוס.main.go: בנו ומיינו את מלאי המוצרים.קראו מצב מיון (
priceאוquantity), ואז קראו מספר ואחריו פרטי מוצרים. כל מוצר מסופק בשלוש שורות: שם, מחיר וכמות.צרו פרוסה של מוצרים, מיינו אותם באמצעות טיפוס המיון המתאים בהתאם למצב, ואז הדפיסו כל מוצר בפורמט הבא:
[Name]: $[Price] (x[Quantity])הציגו מחירים עם שתי ספרות אחרי הנקודה העשרונית.
הקלטים הבאים יסופקו:
- שורה 1: מצב מיון (
priceאוquantity) - שורה 2: מספר המוצרים
- השורות הבאות: פרטי המוצרים (שם, מחיר, כמות - שלוש שורות לכל מוצר)
לדוגמה, עבור הקלט:
price
3
Laptop
999.99
5
Mouse
29.99
50
Keyboard
79.99
25הפלט שלכם אמור להיות:
Mouse: $29.99 (x50)
Keyboard: $79.99 (x25)
Laptop: $999.99 (x5)ועבור הקלט:
quantity
3
Laptop
999.99
5
Mouse
29.99
50
Keyboard
79.99
25הפלט שלכם אמור להיות:
Mouse: $29.99 (x50)
Keyboard: $79.99 (x25)
Laptop: $999.99 (x5)שימו לב שאפשר למיין את אותם נתוני מוצרים בדרכים שונות, פשוט באמצעות שימוש בטיפוס בעל שם אחר. לאחר שהטיפוסים שלכם עומדים בדרישות של sort.Interface, הם עובדים בצורה חלקה עם sort.Sort() מהספרייה הסטנדרטית.
נסו בעצמכם
package main
import (
"bufio"
"fmt"
"os"
"sort"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// קרא את מצב המיון
var mode string
fmt.Fscanln(reader, &mode)
// קרא את מספר המוצרים
var count int
fmt.Fscanln(reader, &count)
// קרא את המוצרים
products := make([]Product, count)
for i := 0; i < count; i++ {
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
priceStr, _ := reader.ReadString('\n')
priceStr = strings.TrimSpace(priceStr)
price, _ := strconv.ParseFloat(priceStr, 64)
qtyStr, _ := reader.ReadString('\n')
qtyStr = strings.TrimSpace(qtyStr)
quantity, _ := strconv.Atoi(qtyStr)
products[i] = Product{Name: name, Price: price, Quantity: quantity}
}
// TODO: מיין את המוצרים לפי mode
// אם mode הוא "price", השתמש בטיפוס ByPrice
// אם mode הוא "quantity", השתמש בטיפוס ByQuantity
// השתמש ב-sort.Sort() עם הטיפוס המתאים
// TODO: הדפס כל מוצר בפורמט:
// [Name]: $[Price] (x[Quantity])
// השתמש ב-fmt.Printf עם %.2f לעיצוב המחיר
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
1יסודות OOP ב-Go
קבצים חיצונייםסביבת העבודה והמודולים של Goחבילות וייבואשמות מיוצאים לעומת שמות לא מיוצאיםמבוא ל-OOP ב-GoStructs כמחלקותהגדרת מתודות עבור Structsמקבלי Pointer לעומת Valueאתחול Structפונקציות Constructorחזרה – מחשבון פשוט4ממשקים
מבוא לממשקיםמימוש משתמעממשק כחוזהממשק ריק (any)אימות טיפוסמתג טיפוסיםהרכבת ממשקיםממשקי Stringer ו-Errorסיכום – מחשבון צורות7כימוס
שדות מיוצאים לעומת שדות לא מיוצאיםכימוס ברמת החבילהמתודות Getter ו-Setterהסתרת מידע ב-Goסיכום – רשומות תלמידים10Generics (Go 1.18 ומעלה)
מבוא ל־Genericsפרמטרים של טיפוסיםאילוצי טיפוסיםמבנים גנרייםפתרון עוקף למתודות גנריותסיכום – אוסף גנרי2העמקה בסוגים וב-Structs
סוגים בסיסיים ומורכביםהגדרות של סוגים מותאמים אישיתתגיות StructStructs אנונימייםStructs מקונניםערכי אפס וברירות מחדלסיכום – ספר אנשי קשר5קומפוזיציה במקום ירושה
למה ב-Go אין ירושהיסודות הטמעת Structקידום מתודותהטמעת כמה Structsהטמעה לעומת אגרגציההסתרת מתודות מוטמעותסיכום – היררכיית עובדים8טיפול בשגיאות ו-OOP
ממשק errorסוגי שגיאות מותאמים אישיתעטיפת שגיאות (fmt.Errorf)שגיאות Sentinelerrors.Is() ו-errors.As()Panic, Defer ו-Recoverסיכום – מנתח קבצים11ספרייה סטנדרטית ותכנות מונחה עצמים
io.Reader ו-io.Writersort.Interfaceהממשק fmt.Stringerencoding/json עם מבניםהממשק http.Handlerסיכום – מודלים של REST API3מצביעים וזיכרון
יסודות המצביעים ב-Goמצביעים למבניםהעברה לפי ערך לעומת לפי הפניההפונקציה new()איסוף אשפה ב-Goסיכום - בונה רשימה מקושרת6פולימורפיזם ב-Go
פולימורפיזם באמצעות ממשקיםטיפוס ברווז ב-Goכללי התאמה לממשקאוספים פולימורפייםהזרקת תלויותסיכום - מעבד תשלומים9מקביליות ו-OOP
יסודות GoroutinesChannels ותקשורתChannel עם חוצץ לעומת ללא חוצץפקודת Selectsync.Mutex ו-sync.RWMutexsync.WaitGroupתכנון Struct בטוח לשימוש במקבילסיכום - מאגר עובדים12מושגים מתקדמים בתכנות מונחה עצמים
דפוס האפשרויות הפונקציונליותדפוס Builder ב-Goשרשור מתודותכינויי טיפוסים לעומת הגדרותיסודות Reflectionסקירה כללית של יצירת קודתרגלו בעצמכם: קומפיילר Go אונליין