Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

نمط الحالة

جزء من قسم البرمجة كائنية التوجه في رحلة 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 عبر الإنترنت