תוכן עניינים הקדמה
הכרות עם schemes
תבניות נתונים
תבניות
משפטי בקרה
משתנים לקסיקלים
רקורסיה
קלט/פלט
macros
מבנים
alises and tables ממשק המערכת
מחלקות ואובייקטים
jumps
אי-דטרמיניסטיות
מנועים
shell scripts
אתר ללימוד מקיף> פרק 9 - מבנים

פרק 9 - מבנים


הנושאים בפרק זה:
9.1.     אתחולי ברירת מחדל
9.2.     הגדרת ה-'destruct'


נתונים שבאופן טבעי מקובצים יחדיו נקראים מבנים. ניתן להשתמש בסוגי נתונים מורכבים לייצג מבנים כמו וקטורים רשימות וכו'.
לדוגמא אנו עוסקים בנתונים הקשורים לעץ. האלמנטים של הנתונים או השדות יכולים להיות: גובה, הקף, גיל, צורת העלה וצבע העלה. סה"כ יש לנו 5 שדות.
נתונים אלו יכולים להיות מיוצגים ע"י וקטור בעל 5 אלמנטים. ניתן יהיה לגשת לשדות ע"י הפרוצדורה vector-ref וכן ניתן יהיה לשנות את ערכם ע"י הפרוצדורה !vector-set. בדרך זו אנו מעמיסים על עצמנו ליזכור איזה אינדקס שייך לכל שדה. וכן יכולות להיווצר טעויות במיוחד כאשר מוסיפים שדות.

לכן נשתמש ב-macro-defstruct על מנת להגדיר מבנה נתונים, שהוא ביסודו וקטור, אך הוא מגיע עם מספר פרוצדורות מתאימות היוצרות דוגמאות למבנה, פרוצדורות
היכולות לגשת למשתנים ולשנות אותן.

המבנה עץ יהיה מוגדר כך:

(defstruct tree height girth age leaf-shape leaf-color)

מבנה זה מספק פרוצדורה בונה הנקראת make-tree.
פרוצדורה זו יכולה לגשת לכל אחד מהשדות הנקרא tree.girth ,tree.height וכו'.
פרוצדורה היכולה לשנות את ערכי השדות נקראת set!tree.girth, set!tree.height וכו'.

נשתמש ב-constructor כך:

(define coconut 
  (make-tree 'height 30
             'leaf-shape 'frond
             'age 5))

הארגומנטים של ה-constructor באים בזוגות, שם השדה מלווה באתחול שלו. השדות יכולים להופיע בסדר כלשהו, ויכולים לעתים להיות חסרים, במקרה זה הערכים שלהם לא מוגדרים.
את הפרוצדורות שניגשות לערכים מפעילים כך:

(tree.height coconut) =>  30
(tree.leaf-shape coconut) =>  frond
(tree.girth coconut) =>  <undefined>

הפרוצדורה tree.grith מחזירה ערך לא מוגדר, מכיוון שלא נתנו ערך ל-girth ב-coconut .

את הפרוצדורות שמשנות ערכים מפעילים כך:

(set!tree.height coconut 40)
(set!tree.girth coconut 10)

אם נרצה לגשת לשדות ע"י הפרוצדורות המתאימות נקבל:

(tree.height coconut) =>  40
(tree.girth coconut) =>  10



לתחילת העמוד

9.1 אתחולי ברירת המחדל

אנו יכולים לבצע איתחולים בזמן הגדרת המבנה. במקום לעשות זאת עבור כל דגם, ניתן להניח שצורת העלה שסועה וצבע העלה ירוק הם
ערכי ברירת המחדל. תמיד ניתן לבטל את ברירות המחדל ע"י אתחול מפורש בפרוצדורה make-tree או ע"י שימוש בפרוצדורה המשנה את ערכי השדות אחרי שנוצרו .

לדוגמא:

(defstruct tree height girth age
                (leaf-shape 'frond)
                (leaf-color 'green))

(define palm (make-tree 'height 60))

(tree.height palm) 
=>  60<

(tree.leaf-shape palm) 
=>  frond

(define plantain 
  (make-tree 'height 7
             'leaf-shape 'sheet))
			 
(tree.height plantain) 
=>  7

(tree.leaf-shape plantain) 
=>  sheet

(tree.leaf-color plantain) 
=>  green



לתחילת העמוד

9.2 הגדרת ה-'defstruct'

ההגדרות של defstruct macro הן:

(define-macro defstruct 
  (lambda (s . ff) 
    (let ((s-s (symbol->string s)) (n (length ff))) 
      (let* ((n+1 (+ n 1)) 
             (vv (make-vector n+1))) 
        (let loop ((i 1) (ff ff)) 
          (if (<= i n) 
            (let ((f (car ff))) 
              (vector-set! vv i  
                (if (pair? f) (cadr f) '(if #f #f))) 
              (loop (+ i 1) (cdr ff))))) 
        (let ((ff (map (lambda (f) (if (pair? f) (car f) f)) 
                       ff))) 
          `(begin 
             (define ,(string->symbol  
                       (string-append "make-" s-s)) 
               (lambda fvfv 
                 (let ((st (make-vector ,n+1)) (ff ',ff)) 
                   (vector-set! st 0 ',s) 
                   ,@(let loop ((i 1) (r '())) 
                       (if (>= i n+1) r 
                           (loop (+ i 1) 
                                 (cons `(vector-set! st ,i  
                                          ,(vector-ref vv i)) 
                                       r)))) 
                   (let loop ((fvfv fvfv)) 
                     (if (not (null? fvfv)) 
                         (begin 
                           (vector-set! st  
                               (+ (list-position (car fvfv) ff) 
                                  1) 
                             (cadr fvfv)) 
                           (loop (cddr fvfv))))) 
                   st))) 
             ,@(let loop ((i 1) (procs '())) 
                 (if (>= i n+1) procs 
                     (loop (+ i 1) 
                           (let ((f (symbol->string 
                                     (list-ref ff (- i 1))))) 
                             (cons 
                              `(define ,(string->symbol  
                                         (string-append 
                                          s-s "." f)) 
                                 (lambda (x) (vector-ref x ,i))) 
                              (cons 
                               `(define ,(string->symbol 
                                          (string-append  
                                           "set!" s-s "." f)) 
                                  (lambda (x v)  
                                    (vector-set! x ,i v))) 
                               procs)))))) 
             (define ,(string->symbol (string-append s-s "?")) 
               (lambda (x) 
                 (and (vector? x) 
                      (eqv? (vector-ref x 0) ',s))))))))))