學習軌道
/
R
R
/
練習
/
二十一點
二十一點

二十一點

學習練習

簡介

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 中的函式

base R 中的 switch 相當有限:它只能對單一輸入做完全相符的比對。 這在 1993 年 R 初次發布時看似合理,但以現代用途來說需要改進。

Note

正如 Conditionals 概念中所提到的,tidyverse 套件集合的設計目的,是在不影響向後相容性的前提下,補充(有時取代)base R 的功能。

只要在程式碼最上方加入 library(dplyr)(載入單一套件)或 library(tidyverse)(載入整個集合),就能將 dplyr 套件引入作用域。

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 的一部分。

說明

在這個練習中,我們要模擬一場二十一點遊戲的第一回合。

你會拿到兩張牌,也能看到莊家翻開的那張牌。所有牌都用字串表示,例如 "ace"、"king"、"three"、"two" 等等。每張牌的點數如下:

牌 點數 牌 點數
ace 11 eight 8
two 2 nine 9
three 3 ten 10
four 4 jack 10
five 5 queen 10
six 6 king 10
seven 7 other 0

注意:一般來說,ace 可以算 1 點或 11 點,但為了簡單起見,我們假設它只能算 11 點。

根據你的兩張牌和莊家的牌,遊戲的第一回合有一套策略,你可以從以下做法中選擇:

  • 停牌(S)
  • 要牌(H)
  • 分牌(P)
  • 自動獲勝(W)

這套策略還不是最佳解,不過你要照著你朋友 Alex 一直在開發的策略走,內容如下:

  • 如果你手上有一對 ace,一定要分牌。
  • 如果你拿到二十一點(兩張牌點數加總為 21),而且莊家的牌不是 ace、人頭牌或 ten,你就自動獲勝。如果莊家持有其中任何一張,你就必須停牌,等另一張牌翻開。
  • 如果你的牌點數加總落在 [17, 20] 這個範圍內,就一定要停牌。
  • 如果你的牌點數加總落在 [12, 16] 這個範圍內,除非莊家的牌是 7 或更高,否則一定要停牌;如果莊家是 7 或更高,就一定要要牌。
  • 如果你的牌點數加總為 11 或更低,就一定要要牌。

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,透過 21 個概念111 個練習 和真人引導來學習並精通 R,全部免費。