Menu
Coddy logo textTech

תבנית State

חלק מהיחידה תכנות מונחה עצמים במסלול ה-R של Coddy. שיעור 48 מתוך 57.

אובייקט בתבנית המצב מתנהג באופן שונה בהתאם למצב הנוכחי שלו. כל מצב הוא רשימה של פונקציות מטפלות, והאובייקט מעביר כל בקשה למטפלות של המצב שבו הוא נמצא:

states <- list(
  green = list(label = "go", next_state = "yellow"),
  yellow = list(label = "slow down", next_state = "red"),
  red = list(label = "stop", next_state = "green")
)
current <- "green"
for (i in 1:4) {
  cat(current, ":", states[[current]]$label, "\n")
  current <- states[[current]]$next_state
}

פלט:

green : go 
yellow : slow down 
red : stop 
green : go 

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

states <- list(
  locked = list(
    coin = function() list(to = "unlocked", say = "unlocked"),
    push = function() list(to = "locked", say = "blocked")
  ),
  unlocked = list(
    coin = function() list(to = "unlocked", say = "already open"),
    push = function() list(to = "locked", say = "welcome")
  )
)
current <- "locked"
for (event in c("push", "coin", "coin", "push")) {
  r <- states[[current]][[event]]()
  current <- r$to
  cat(event, "->", r$say, "\n")
}

פלט:

push -> blocked 
coin -> unlocked 
coin -> already open 
push -> welcome 

המכונה עצמה היא סגירה קטנה: היא מחזיקה את המצב הנוכחי, מחפשת את המטפל, ומשיבה ignored עבור אירוע שהמצב אינו מכיר:

make_machine <- function(states, start) {
  current <- start
  list(
    handle = function(event) {
      h <- states[[current]][[event]]
      if (is.null(h)) return("ignored")
      r <- h()
      current <<- r$to
      r$say
    },
    state = function() current
  )
}
states <- list(off = list(press = function() list(to = "on", say = "light on")),
               on = list(press = function() list(to = "off", say = "light off")))
m <- make_machine(states, "off")
for (e in c("press", "kick", "press")) cat(e, ":", m$handle(e), "|", m$state(), "\n")

פלט:

press : light on | on 
kick : ignored | on 
press : light off | off 

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

states <- list(
  off = list(press = function() list(to = "on", say = "on")),
  on = list(press = function() list(to = "off", say = "off"),
            overheat = function() list(to = "broken", say = "fuse blown")),
  broken = list(repair = function() list(to = "off", say = "repaired"))
)
current <- "off"
for (e in c("press", "overheat", "press", "repair")) {
  h <- states[[current]][[e]]
  if (is.null(h)) { cat(e, ": ignored\n"); next }
  r <- h()
  current <- r$to
  cat(e, ":", r$say, "\n")
}

פלט:

press : on 
overheat : fuse blown 
press : ignored
repair : repaired 
challenge icon

אתגר

קל

השלימו את הקרוסלה ב-Turnstile.R. הרשימה states מכילה את המצבים locked ו-unlocked, וכל מטפל מחזיר list(to = ..., say = ...):

  • locked: coin פותח אותה באמצעות unlocked; push משאיר אותה נעולה באמצעות blocked;
  • unlocked: coin משאיר אותה פתוחה באמצעות thanks, already open; push נועל אותה באמצעות welcome וסופר מעבר אחד.

לאחר מכן השלימו את make_turnstile(), שמתחילה במצב נעול ומחזירה את handle(event) (התשובה, או ignored במקרה של אירוע לא מוכר), את state() ואת passes(). ספרו מעבר בכל פעם ש-push מטופל במצב הפתוח. הקוד שסופק קורא אירועים ומדפיס כל תשובה לצד המצב החדש, ולאחר מכן את מספר המעברים.

כתבו את הקוד שלכם ב-Turnstile.R. הקובץ main.R מכיל את קוד הקלט/פלט שסופק, ואי אפשר לערוך אותו.

נסו בעצמכם

source("Turnstile.R")

# קוד הקלט/פלט שסופק: יש להשאיר אותו כפי שהוא
input <- suppressWarnings(readLines(file("stdin")))
t <- make_turnstile()
for (event in input) cat(event, ": ", t$handle(event), " [", t$state(), "]\n", sep = "")
cat("passes: ", t$passes(), "\n", sep = "")
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר R אונליין