بلک‌جک

بلک‌جک

تمرین یادگیری

مقدمه

تابع switch() می‌تواند جایگزینی مختصر برای مجموعه‌ای طولانی از شرط‌های if ... else if باشد. متغیری که روی آن سوئیچ انجام می‌شود معمولاً یک رشته است و در این صورت می‌توان کوتیشن‌ها را از انتخابگر حذف کرد.

star <- function(type) {
  switch(type,
    M = , # will "fall through" if no value given
    K = "red dwarf",
    G = "Earth-like",
    "bigger star" # only correct for O,B,A,F
  )
}

> star("M")
[1] "red dwarf"

توجه کنید که گزینه‌ها فقط زمانی به گزینه‌ی بعدی می‌رسند که مقدار خالی گذاشته شده باشد، مانند M در مثال بالا. لازم نیست دستورهای break را مانند برخی زبان‌های دیگر بگنجانید.

وقتی روی نوع‌های character سوئیچ می‌کنید، مقدار نهایی یک پیش‌فرض است، همان‌طور که اینجا می‌بینید. با نوع‌های عدد صحیح، هیچ پیش‌فرضی نمی‌توان داد.

توابع در dplyr

switch در R پایه نسبتاً محدود است: فقط تطبیق دقیق با یک ورودی واحد را انجام می‌دهد. این در سال ۱۹۹۳ که R برای اولین بار منتشر شد معقول به نظر می‌رسید، اما برای استفاده‌ی مدرن نیاز به بهبود دارد.

Note

همان‌طور که در مفهوم Conditionals اشاره شد، مجموعه‌ی بسته‌های tidyverse طوری طراحی شده است که قابلیت‌های R پایه را تکمیل کند (و گاهی جایگزین آن شود) بدون اینکه سازگاری با نسخه‌های قبلی را تحت تأثیر قرار دهد.

بسته‌ی dplyr را می‌توان با افزودن library(dplyr) (برای همان بسته) یا library(tidyverse) (برای کل مجموعه) به بالای کدتان در دسترس قرار داد.

کتابخانه‌ی dplyr دو تابع اضافی مرتبط با switch ارائه می‌دهد.

توابع recode_values و replace_values

این دو تابع مرتبط امکان نگاشت برداری شبیه به switch را از مقادیر قدیمی به مقادیر جدید فراهم می‌کنند.

تفاوت اصلی آن‌ها این است که recode_values() یک بردار کاملاً جدید می‌سازد، در حالی که replace_values() به‌روزرسانی جزئی یک بردار موجود را ممکن می‌کند.

تطبیق همچنان دقیق است، اما:

  • گزینه‌های سمت چپ می‌توانند بردار باشند و اگر هر عنصری تطبیق کند، ورودی تطبیق داده می‌شود.
  • گزینه‌های سمت چپ و نتایج سمت راست با ~ به جای = به هم مرتبط می‌شوند.
  • در همه‌ی حالت‌ها می‌توان یک پیش‌فرض مشخص کرد.
library(dplyr)

x <- c("a", "b", "a", "d", "b", NA, "c", "e", "z")
recode_values(
  x,
  "a" ~ 1,
  "b" ~ 2,
  "c" ~ 3,
  c("d", "e") ~ 4, # either "d' or "e" will match
  NA ~ 0,          # matches missing values
  default = 100   # note the different syntax for the default
)
#> [1]   1   2   1   4   2   0   3   4 100

تابع case_when

case_when نحوه‌ی نگارش case_match را یک مرحله فراتر می‌برد و اجازه می‌دهد هر عبارت منطقی در سمت چپ ~ قرار بگیرد.

بردار ورودی (x در مثال زیر) به عنوان آرگومان ارائه نمی‌شود؛ فقط باید از قبل تعریف شده باشد.

x <- 1:10
case_when(
  x < 3 ~ "low",
  between(x, 3, 5) ~ "mid",
  between(x, 6, 8) ~ "high",
  .default = "what?"
)
#>  [1] "low"   "low"   "mid"   "mid"   "mid"   "high"  "high"  "high"  "what?" "what?"

تابع between() نیز بخشی از dplyr است.

دستورالعمل‌ها

در این تمرین اولین نوبت یک بازی Blackjack را شبیه‌سازی می‌کنیم.

شما دو کارت دریافت می‌کنید و می‌توانید کارت نمایان دیلر را ببینید. همه‌ی کارت‌ها با «رشته»ای مانند «ace»، «king»، «three»، «two» و غیره نمایش داده می‌شوند. ارزش هر کارت به این صورت است:

کارت ارزش کارت ارزش
ace ۱۱ eight ۸
two ۲ nine ۹
three ۳ ten ۱۰
four ۴ jack ۱۰
five ۵ queen ۱۰
six ۶ king ۱۰
seven ۷ other ۰

نکته: معمولاً آس‌ها می‌توانند ارزش ۱ یا ۱۱ داشته باشند، اما برای سادگی فرض می‌کنیم که فقط ارزش ۱۱ را می‌گیرند.

بسته به دو کارت شما و کارت دیلر، برای اولین نوبت بازی یک استراتژی وجود دارد که در آن این گزینه‌ها را دارید:

  • ایستادن (S)
  • کارت گرفتن (H)
  • تقسیم کردن (P)
  • برد خودکار (W)

هرچند هنوز بهینه نیست، شما از استراتژی‌ای پیروی می‌کنید که دوستتان Alex مشغول توسعه‌ی آن بوده است؛ این استراتژی به شرح زیر است:

  • اگر یک جفت آس داشته باشید، همیشه باید آن‌ها را تقسیم کنید.
  • اگر Blackjack داشته باشید (دو کارتی که مجموعشان ۲۱ می‌شود) و دیلر آس، کارت تصویری یا ده نداشته باشد، به‌طور خودکار برنده می‌شوید. اگر دیلر یکی از این کارت‌ها را داشته باشد، باید بایستید و منتظر بمانید تا کارت دیگر رو شود.
  • اگر مجموع کارت‌هایتان در بازه‌ی [۱۷، ۲۰] باشد، همیشه باید بایستید.
  • اگر مجموع کارت‌هایتان در بازه‌ی [۱۲، ۱۶] باشد، همیشه باید بایستید، مگر اینکه دیلر ۷ یا بالاتر داشته باشد؛ در آن صورت همیشه باید کارت بگیرید.
  • اگر مجموع کارت‌هایتان ۱۱ یا کمتر باشد، همیشه باید کارت بگیرید.

1. ارزش هر کارت را محاسبه کنید.

تابعی به نام parse_card پیاده‌سازی کنید که ارزش عددی یک کارت را محاسبه می‌کند:

parse_card("ace")
# => 11

2. منطق تصمیم‌گیری برای اولین نوبت را پیاده‌سازی کنید.

تابعی به نام first_turn بنویسید که منطق تصمیم‌گیری را همان‌طور که در بالا توضیح داده شد پیاده‌سازی می‌کند:

first_turn(card1, card2, dealer_card)

در ادامه چند نمونه از نتایج مورد انتظار را می‌بینید:

first_turn("ace", "ace", "jack") == "P"
first_turn("ace", "king", "ace") == "S"
first_turn("five", "queen", "ace") == "H"
ویرایش از طریق GitHub این لینک در پنجره یا زبانه‌ی جدیدی باز می‌شود
R Exercism

آماده‌اید بلک‌جک را شروع کنید؟

در Exercism ثبت‌نام کنید تا R را همراه با 21 مفهوم111 تمرین و مربی‌گری انسانی واقعی یاد بگیرید و در آن استاد شوید، همه‌ی این‌ها رایگان.