0Pricing
R Academy · บทเรียน

การเขียนฟังก์ชันที่ทนทานด้วย stop() และ warning()

ส่งสัญญาณเงื่อนไขแบบกำหนดเองจากภายในฟังก์ชันของคุณเอง

การเขียนฟังก์ชันที่ทนทานด้วย stop() และ warning() เป็นบทเรียน R Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน R Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส R Academy มีบทเรียนทั้งหมด 4 บทเรียน

เหตุใดการตรวจสอบข้อมูลเข้าจึงสำคัญ

ฟังก์ชันเชิงป้องกันจะตรวจสอบข้อมูลเข้าและล้มเหลวอย่างชัดเจนพร้อมข้อผิดพลาดที่ให้ข้อมูล แทนที่จะสร้างผลลัพธ์ที่ผิดโดยไม่แจ้งให้ทราบ ฟังก์ชันที่ดักจับข้อมูลเข้าที่ไม่ถูกต้องตั้งแต่ขอบเขต จะดีบักได้ง่ายกว่าฟังก์ชันที่ปล่อยให้ข้อมูลขยะไหลผ่านกระบวนการต่อเนื่อง

# Without validation: wrong result, no error
bad_mean <- function(x) sum(x) / length(x)
bad_mean('hello')   # no error, just NA

# With validation: clear error
good_mean <- function(x) {
  if (!is.numeric(x)) stop('x must be numeric')
  sum(x) / length(x)
}
tryCatch(good_mean('hello'), error = function(e) cat(e$message, '\n'))

ข้อความข้อผิดพลาดที่ให้ข้อมูล

ข้อผิดพลาดทั่วไปอย่าง stop('bad input') ทำให้ดีบักได้ยาก ใช้ paste0() หรือ sprintf() ภายใน stop() เพื่อใส่ค่าจริงและประเภทที่คาดไว้ลงในข้อความ วิธีนี้ช่วยประหยัดเวลาเมื่อติดตามข้อผิดพลาดในกระบวนการต่อเนื่อง

check_numeric <- function(x, arg_name = 'x') {
  if (!is.numeric(x)) {
    stop(sprintf(
      "'%s' must be numeric, but got class '%s'",
      arg_name, class(x)
    ))
  }
}
tryCatch(check_numeric('abc', 'score'),
  error = function(e) cat(e$message, '\n'))

การตรวจสอบความยาวและช่วงค่า

การตรวจสอบที่พบบ่อย ได้แก่ ตรวจสอบว่าเวกเตอร์ไม่ว่าง (length(x) == 0) ตรวจสอบว่าสเกลาร์อยู่ในช่วงที่กำหนด หรือ ตรวจสอบว่าตารางข้อมูลมีคอลัมน์ตามที่คาดไว้ การตรวจสอบแต่ละรายการควรมีข้อความข้อผิดพลาดที่ออกแบบให้เหมาะกับกรณีนั้น

validate_prob <- function(p) {
  if (!is.numeric(p)) stop(paste0('p must be numeric, got: ', class(p)))
  if (length(p) != 1) stop(paste0('p must be length 1, got: ', length(p)))
  if (p < 0 || p > 1) stop(paste0('p must be in [0,1], got: ', p))
  p
}
cat(validate_prob(0.7), '\n')
tryCatch(validate_prob(1.5), error = function(e) cat(e$message, '\n'))

การใช้ warning() สำหรับปัญหาที่ไม่ร้ายแรง

ใช้ warning() เมื่อฟังก์ชันยังสามารถคืนผลลัพธ์ที่เหมาะสมได้ แต่ผู้เรียกควรทราบว่ามีสิ่งผิดปกติเกิดขึ้น เช่น การแปลงค่า การเติมค่าเริ่มต้น หรือการตรวจพบข้อมูลเข้าที่อยู่ใกล้ขอบเขต

clamp <- function(x, lo, hi) {
  if (any(x < lo)) warning(paste0(sum(x < lo), ' value(s) below lo, clamped'))
  if (any(x > hi)) warning(paste0(sum(x > hi), ' value(s) above hi, clamped'))
  pmax(lo, pmin(hi, x))
}
result <- clamp(c(2, -5, 7, 100, 4), 0, 10)
cat('Result:', result, '\n')

call. = FALSE ใน stop()

ตามค่าเริ่มต้น stop() จะเติม Error in funcname(...): ไว้หน้าข้อความ ส่ง call. = FALSE เพื่อระงับคำนำหน้านี้เมื่อบริบทของผู้เรียกชัดเจนอยู่แล้ว หรือเมื่อฟังก์ชันนั้นเป็นเครื่องมือที่ผู้ใช้เรียกโดยตรง

# With call. = TRUE (default): shows calling function
f1 <- function(x) stop('bad input')
tryCatch(f1(1), error = function(e) cat(conditionMessage(e), '\n'))

# With call. = FALSE: cleaner message
f2 <- function(x) stop('bad input', call. = FALSE)
tryCatch(f2(1), error = function(e) cat(conditionMessage(e), '\n'))

คลาสเงื่อนไขแบบกำหนดเองสำหรับ stop()

สร้างคลาสข้อผิดพลาดแบบกำหนดเองด้วย structure() เพื่อให้ผู้เรียกดักจับเฉพาะข้อผิดพลาดจากฟังก์ชันของคุณ โดยไม่ดักจับข้อผิดพลาดทั้งหมด นี่คือแนวทางระดับมืออาชีพสำหรับการเขียนข้อผิดพลาดในระดับแพ็กเกจ

value_error <- function(msg, call = sys.call(-1)) {
  structure(
    class = c('value_error', 'error', 'condition'),
    list(message = msg, call = call)
  )
}
check_positive <- function(x) {
  if (x <= 0) stop(value_error(paste0('Expected positive, got: ', x)))
  x
}
tryCatch(check_positive(-3),
  value_error = function(e) cat('ValueError:', e$message, '\n'),
  error        = function(e) cat('Other error:', e$message, '\n'))

การตรวจสอบคอลัมน์ของตารางข้อมูล

เมื่อฟังก์ชันต้องการตารางข้อมูล ให้ตรวจสอบว่าคอลัมน์ที่จำเป็นมีอยู่ก่อนนำไปใช้ ใช้ %in% เพื่อตรวจสอบชื่อคอลัมน์ และใช้ setdiff() เพื่อรายงานว่าคอลัมน์ใดหายไป

require_cols <- function(df, cols) {
  missing <- setdiff(cols, names(df))
  if (length(missing) > 0) {
    stop(paste0('Missing columns: ', paste(missing, collapse = ', ')))
  }
  invisible(df)
}
df <- data.frame(x = 1:3, y = 4:6)
tryCatch(
  require_cols(df, c('x', 'z', 'w')),
  error = function(e) cat(e$message, '\n')
)

stopifnot() สำหรับการยืนยันแบบกระชับ

stopifnot(condition1, condition2, ...) จะโยนข้อผิดพลาดหากเงื่อนไขใด ๆ เป็น FALSE วิธีนี้เป็นแนวทางกระชับสำหรับยืนยันเงื่อนไขก่อนเริ่มทำงานที่ด้านบนของฟังก์ชัน โดยไม่ต้องเขียนบล็อก if (!...) stop(...) แยกกัน

compute_area <- function(width, height) {
  stopifnot(
    is.numeric(width),
    is.numeric(height),
    width > 0,
    height > 0
  )
  width * height
}
cat(compute_area(5, 3), '\n')
tryCatch(compute_area(-1, 3), error = function(e) cat(e$message, '\n'))

stopifnot() แบบตั้งชื่อเพื่อข้อความที่ดีขึ้น

ใน R 4.0+ นิพจน์ที่ตั้งชื่อภายใน stopifnot() จะแทนที่ข้อความที่สร้างขึ้นโดยอัตโนมัติด้วยคำอธิบายที่คุณกำหนดเอง ใช้ข้อความของนิพจน์เป็นชื่อเพื่อสร้างข้อความแจ้งความล้มเหลวที่ให้ข้อมูล

validate_age <- function(age) {
  stopifnot(
    'age must be numeric'    = is.numeric(age),
    'age must be positive'   = age > 0,
    'age must be under 150'  = age < 150
  )
  invisible(age)
}
tryCatch(validate_age(-5), error = function(e) cat(e$message, '\n'))
tryCatch(validate_age('x'), error = function(e) cat(e$message, '\n'))

การรวมการตรวจสอบกับตรรกะทางธุรกิจ

ฟังก์ชันที่มีโครงสร้างดีจะแยกการตรวจสอบออกจากตรรกะ ให้ตรวจสอบที่ด้านบน จากนั้นจึงดำเนินการ วิธีนี้ทำให้ฟังก์ชันอ่านง่ายขึ้น และทำให้แน่ใจว่าตรวจพบข้อผิดพลาดก่อนเริ่มการคำนวณ

discount_price <- function(price, pct) {
  if (!is.numeric(price) || price <= 0)
    stop(paste0('price must be positive numeric, got: ', price))
  if (!is.numeric(pct) || pct < 0 || pct > 100)
    stop(paste0('pct must be in [0,100], got: ', pct))
  if (pct > 50) warning('discount > 50% is unusual')
  price * (1 - pct / 100)
}
cat(discount_price(100, 20), '\n')
cat(suppressWarnings(discount_price(100, 60)), '\n')

tryCatch ภายในฟังก์ชันอรรถประโยชน์

บางครั้งฟังก์ชันอรรถประโยชน์ควรจัดการข้อผิดพลาดภายในและคืนค่า fallback แทนการส่งต่อข้อผิดพลาด ให้ครอบตรรกะหลักด้วย tryCatch() และคืนค่าบ่งชี้ที่ตกลงกันไว้ เช่น NA หรือ NULL เมื่อทำงานล้มเหลว

safe_log <- function(x) {
  tryCatch({
    if (!is.numeric(x)) stop('not numeric')
    if (x <= 0) stop('must be positive')
    log(x)
  }, error = function(e) {
    warning(paste0('safe_log failed for x=', x, ': ', e$message))
    NA_real_
  })
}
results <- sapply(list(10, -1, 'a', 100), safe_log)
cat(results, '\n')

ตรวจสอบอย่างรวดเร็ว

call. = FALSE ทำอะไรเมื่อส่งให้ stop()

ประเด็นสำคัญของฟังก์ชันที่ทนทาน

ประเด็นสำคัญสำหรับการเขียนฟังก์ชันที่ทนทาน:

  • ตรวจสอบข้อมูลเข้าที่ด้านบน และล้มเหลวอย่างชัดเจนด้วยข้อความจาก stop() ที่ให้ข้อมูล
  • ใส่ค่าจริงไว้ในข้อความข้อผิดพลาด: paste0('Expected numeric, got: ', class(x))
  • ใช้ warning() สำหรับปัญหาที่ไม่ร้ายแรงซึ่งยังดำเนินการคำนวณต่อได้
  • call. = FALSE ระงับคำนำหน้าฟังก์ชันผู้เรียกในข้อความข้อผิดพลาด
  • ใช้ stopifnot() สำหรับการยืนยันแบบกระชับ และใช้รูปแบบตั้งชื่อสำหรับข้อความแบบกำหนดเอง (R 4.0+)
  • คลาสเงื่อนไขแบบกำหนดเองช่วยให้ผู้เรียกดักจับได้อย่างเลือกเจาะจง
robust_divide <- function(x, y) {
  stopifnot('x must be numeric' = is.numeric(x),
            'y must be numeric' = is.numeric(y))
  if (y == 0) stop('Division by zero', call. = FALSE)
  if (abs(y) < 1e-10) warning('y is very small; result may be inaccurate')
  x / y
}
cat(robust_divide(10, 2), '\n')
tryCatch(robust_divide(10, 0), error = function(e) cat(e$message, '\n'))

คำถามที่พบบ่อย

บทเรียน “การเขียนฟังก์ชันที่ทนทานด้วย stop() และ warning()” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การเขียนฟังก์ชันที่ทนทานด้วย stop() และ warning()” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส R Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส R Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การเขียนฟังก์ชันที่ทนทานด้วย stop() และ warning()”

ส่งสัญญาณเงื่อนไขแบบกำหนดเองจากภายในฟังก์ชันของคุณเอง คุณปฏิบัติ R Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน R Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน R Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “การเขียนฟังก์ชันที่ทนทานด้วย stop() และ warning()” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน R Academy นี้ได้ไหม

ได้ บทเรียน R Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. ข้อผิดพลาด คำเตือน และข้อความใน R
  2. tryCatch() สำหรับการกู้คืนจากข้อผิดพลาด
  3. withCallingHandlers() และการเริ่มทำงานใหม่
  4. การเขียนฟังก์ชันที่ทนทานด้วย stop() และ warning()
← กลับไปที่ R Academy