在 R 語言中,感嘆號 ! 不只是邏輯運算子,當它變成兩個或三個時,更是資料處理與動態程式設計的神器。特別是在 dplyr 與 rlang 這類進階套件中,!! 與 !!! 能幫助我們靈活地操作變數與參數。這篇文章將帶你一步步搞懂它們的差異與用法。
單重感嘆號 !:邏輯否定
最基礎的用法,就是用來表示「否定」,例如 !TRUE 會回傳 FALSE,是條件判斷中的常見運算符。!TRUE # FALSE
!FALSE # TRUE
雙重感嘆號 !!優勢
雙重感嘆號 最常用在 自定義函數的動態操作欄位 , 過去傳統寫法的痛點,無法動態操作欄位
🎯 情境:寫一個可以計算平均值的函數
❌ 一般傳統寫法(會出問題)
my_mean <- function(data, col) {
data %>%
summarise(mean_value = mean(col))
}
my_mean(mtcars, mpg)
👉 問題:col 被當成「普通變數」,而不是資料框中的欄位,結果會錯或報錯。
Error in `summarise()`: ℹ In argument: `mean_value = mean(col)`. Caused by error: ! 找不到物件 'mpg' Run `rlang::last_trace()` to see where the error occurred.
😓 傳統可運行寫法(很醜)
my_mean <- function(data, col_name) {
data %>%
summarise(mean_value = mean(.data[[col_name]]))
}
my_mean(mtcars, "mpg")
👉 問題:必須用字串 "mpg",可讀性差,不符合 dplyr 的語法風格,寫起來不直覺
⚡ 使用 !!:優雅解決「動態欄位」問題
library(dplyr)
library(rlang)
# 改寫後
my_mean <- function(data, col) {
data %>%
summarise(mean_value = mean(!!enquo(col)))
}
my_mean(mtcars, mpg)
my_mean(mtcars, hp)
#動態 group_by
my_summary <- function(data, group_var, target_var) {
data %>%
group_by(!!enquo(group_var)) %>%
summarise(mean_value = mean(!!enquo(target_var)))
}
my_summary(mtcars, cyl, mpg)
my_summary(mtcars, gear, hp)
👉 !! 的核心價值,讓你可以用「欄位名稱」而不是字串,寫出可重複使用的函數
enquo(col) # 先把欄位「包起來」
!! # 再把它「打開來用」
三重感嘆號 !!!優勢
雖然雙重!!很好用,但仍有其限制,一次只能計算一個統計值,而三重感嘆號可以同時計算多個統計量,其實最白話意思 !!! = 把一個 list 裡的東西「拆開丟進函數」
❌ 一般傳統寫法(寫死)
mtcars %>%
summarise(
mean_mpg = mean(mpg),
sd_mpg = sd(mpg),
max_mpg = max(mpg)
)
👉 問題:每個統計量都要手寫,無法根據需求動態調整,不適合寫函數。
🚀 使用 !!!:動態展開多個參數
✅ 改寫後(動態生成)
library(purrr)
funs <- list(
mean = mean,
sd = sd,
max = max
)
mtcars %>%
summarise(
!!!imap(funs, ~ set_names(list(.x(mtcars$mpg)), paste0(.y, "_mpg")))
)
--------------------------------------
mean sd max
1 20.09062 6.026948 33.9
總結:三個驚嘆號的本質差異

















