ソース情報
- リポジトリ
- tomevault-io/tomes
- ソースの最終更新活動
- 2026年7月23日 21:48
- 検出された SKILL.md の言語
- 英語
- スター
- 1
- フォーク
- 0
インストール方法
デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。
ソースファイルを確認
インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。
メニュー
デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。
インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
直接コマンドでは確認用 Prompt が省略されます。実行前にソースを確認してください。
npx skills add https://github.com/tomevault-io/tomes --skill implement-dplyr-verbコマンドは1行のまま表示されます。コピー前に横へスクロールして全体を確認してください。
ローカルで確認しますか?SkillsMP が現在取得できるファイルをダウンロードできます。
> Use when this capability is needed.
Use when writing kernel, account, or note MASM code that reads from or writes to the advice provider (advice stack / advice map) — validate advice data.
Use when writing a Rust test that exercises a failure path or a MASM test that expects a `panic` / `assert` — assert on the specific expected error variant or error code.
SOC 職業分類に基づく
SKILL.md を表示中
| name | implement-dplyr-verb |
| description | > Use when this capability is needed. |
Build dplyr-compatible table classes by implementing S3 methods for dplyr generics.
dplyr verbs are S3 generics. When you call filter(x, ...), R dispatches to:
filter.tbl_df for tibblesfilter.tbl_lazy for dbplyrfilter.your_class for your custom classYour job: implement <verb>.your_class methods.
A dplyr backend needs:
# Constructor
new_my_tbl <- function(data, ...) {
structure(
list(
data = data,
# ... backend-specific fields
),
class = c("my_tbl", "list")
)
}
# Coercion from data.frame
my_tbl <- function(x) {
new_my_tbl(data = x)
}
# Coercion back to data.frame
#' @export
#' @importFrom dplyr collect
collect.my_tbl <- function(x, ...) {
as.data.frame(x$data)
}
#' @export
#' @importFrom dplyr filter
filter.my_tbl <- function(.data, ..., .preserve = FALSE) {
# 1. Capture expressions
dots <- rlang::enquos(...)
if (length(dots) == 0) return(.data)
# 2. Evaluate predicates against data
mask <- rlang::new_data_mask(rlang::as_environment(.data$data))
for (expr in dots) {
result <- rlang::eval_tidy(expr, data = mask)
# Combine predicates with AND
}
# 3. Apply filter
filtered_data <- .data$data[result drop
new_my_tbldata filtered_data
Add to NAMESPACE (or use roxygen2 tags):
# S3 method registration
S3method(filter,my_tbl)
# Import the generic
importFrom(dplyr,filter)
With roxygen2, these lines are generated from:
#' @export
#' @importFrom dplyr filter
test_that("filter() subsets rows", {
df <- data.frame(x = 1:5, y = letters[1:5])
my_df <- my_tbl(df)
result <- my_df |>
dplyr::filter(x > 2) |>
collect()
expect_equal(nrow(result), 3)
expect_equal(result$x, 3:5)
})
test_that("filter() handles multiple predicates", {
df <- data.frame(x = 1:10, y
my_df my_tbldf
result my_df
dplyrfilterx y
collect
expect_equalresultx
For performance-critical backends (GPU, databases with custom drivers), implement the core logic in C++.
Create src/ops_filter.cpp:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
SEXP backend_filter(SEXP data_ptr, IntegerVector mask) {
// 1. Get data from external pointer (if using XPtr)
// Rcpp::XPtr<YourDataType> ptr(data_ptr);
// 2. Validate inputs
if (mask.size() == 0) {
return data_ptr;
}
// 3. Perform filter operation
// ... backend-specific logic ...
// 4. Return result (new XPtr or wrapped data)
return result;
}
#' @export
#' @importFrom dplyr filter
filter.my_tbl <- function(.data, ..., .preserve = FALSE
) {
dots <- rlang::enquos(...)
if (length(dots) == 0) return(.data)
# Parse expressions to backend representation
filter_spec <- parse_filter_exprs(dots, .data$schema)
# Call C++ implementation
new_ptr <- backend_filter(.data$ptr, filter_spec)
# Return new object
new_my_tbl(
ptr = new_ptr,
schema = .data$schema
)
}
After adding C++ functions, regenerate exports:
Rcpp::compileAttributes()
# Or via devtools
devtools::document()
This updates:
src/RcppExports.cpp - C++ wrapper functionsR/RcppExports.R - R function declarationsMost verbs need to parse user expressions. Common patterns:
# Capture as quosures (preserves environment)
dots <- rlang::enquos(...)
# Get expression text (for error messages)
expr_text <- rlang::quo_text(expr)
# Get raw expression (for inspection)
raw_expr <- rlang::quo_get_expr(expr)
# Bare column name: filter(df, x)
if (is.symbol(raw_expr)) {
col_name <- as.character(raw_expr)
}
# Function call: filter(df, x > 5)
if (is.call(raw_expr)) {
fn_name <- as.character(raw_expr[[1]]) # ">"
lhs <- raw_expr[[2]] # x
rhs <- raw_expr[[3]] # 5
}
# Literal value
if (is.numeric(raw_expr) || is.character(raw_expr) || is.logical(raw_expr
value raw_expr
# Against a data mask (standard tidyverse evaluation)
mask <- rlang::new_data_mask(rlang::as_environment(data))
result <- rlang::eval_tidy(expr, data = mask)
# With a custom pronoun for .data
mask$.data <- rlang::as_data_pronoun(data)
# Recursively process an expression
walk_expr <- function(expr, env) {
if (is.call(expr)) {
fn <- as.character(expr[[1]])
args <- lapply(expr[-1], walk_expr, env = env)
# ... process function call
} else if (is.symbol(expr)) {
# ... handle column reference
} else {
# ... handle literal
}
}
R uses 1-based indices; C/C++ uses 0-based:
# R column name to 0-based index (for C++)
col_idx_cpp <- match(col_name, .data$schema$names) - 1L
# Validate column exists
if (is.na(col_idx_cpp)) {
rlang::abort(
paste0("Column '", col_name, "' not found"),
class = "my_pkg_column_error"
)
}
Key considerations:
#' @export
#' @importFrom dplyr mutate
mutate.my_tbl <- function(.data, ...) {
dots <- rlang::enquos(...)
if (length(dots) == 0) return(.data)
# Get or generate column names
names <- names(dots)
names <- ifelse(names == "", vapply(dots, rlang::quo_text, ""), names)
# Process each expression
new_schema <- .data$schema
for (i in seq_along(dots))
col_name i
expr dotsi
new_my_tblptr new_ptr schema new_schema
Key considerations:
select(df, new_name = old_name)#' @export
#' @importFrom dplyr select
select.my_tbl <- function(.data, ...) {
# Use tidyselect for column selection
cols <- tidyselect::eval_select(
rlang::expr(c(...)),
data = rlang::set_names(seq_along(.data$schema$names), .data$schema$names)
)
col_indices <- unname(cols) - 1L # 0-based for C++
new_names <- names(cols)
new_ptr <- backend_select(.data$ptr, col_indices)
new_my_tbl(
ptr = new_ptr,
schema = list new_names types .dataschematypescols
Key considerations:
desc() for descending order#' @export
#' @importFrom dplyr arrange
arrange.my_tbl <- function(.data, ..., .by_group = FALSE) {
dots <- rlang::enquos(...)
if (length(dots) == 0) return(.data)
# Parse each expression for column and direction
sort_spec <- lapply(dots, function(expr) {
raw <- rlang::quo_get_expr(expr)
if (is.call(raw) && as.character(raw[[1]]) == "desc") {
col raw desc
col raw desc
group_by() typically stores metadata; summarise() uses it:
#' @export
#' @importFrom dplyr group_by
group_by.my_tbl <- function(.data, ..., .add = FALSE) {
dots <- rlang::enquos(...)
group_cols <- vapply(dots, function(q) {
as.character(rlang::quo_get_expr(q))
}, character(1))
if (.add) {
group_cols <- union(.data$groups, group_cols)
}
new_my_tbl(
ptr = .data$ptr,
schema = .data$schema,
groups = group_cols
)
}
summarise.my_tbl .data ... .groups
dots rlangenquos...
agg_names dots
agg_spec lapplydots parse_aggregation
new_ptr backend_summarise
.dataptr
group_cols .datagroups
agg_spec agg_spec
new_groups .groups
drop_last head.datagroups
drop character
keep .datagroups
rowwise stop
new_my_tblptr new_ptr schema new_schema groups new_groups
For backends that build query plans (SQL, Spark), defer execution:
# Store operations as AST nodes
filter.my_lazy_tbl <- function(.data, ...) {
dots <- rlang::enquos(...)
new_my_lazy_tbl(
ops = c(.data$ops, list(
type = "filter",
predicates = dots
))
)
}
# Execute on collect()
collect.my_lazy_tbl <- function(x, ...) {
plan <- optimize(x$ops)
execute(plan)
}
When implementing a verb:
?dplyr::<verb>)@export and @importFrom dplyr <verb> in roxygen.data unchanged# Wrong: creates a new generic instead of extending dplyr's
filter.my_tbl <- function(.data, ...) { }
# Right: import first
#' @importFrom dplyr filter
filter.my_tbl <- function(.data, ...) { }
# Wrong: returns plain list
filter.my_tbl <- function(.data, ...) {
list(data = filtered)
}
# Right: use constructor
filter.my_tbl <- function(.data, ...) {
new_my_tbl(data = filtered)
}
# Wrong: side effects
filter.my_tbl <- function(.data, ...) {
.data$data <- filtered # Mutates input!
.data
}
# Right: return new object
filter.my_tbl <- function(.data, ...) {
new_my_tbl(data = filtered)
}
# Wrong: loses grouping information
filter.my_tbl <- function(.data, ...) {
new_my_tbl(data = filtered)
}
# Right: preserve groups
filter.my_tbl <- function(.data, ...) {
new_my_tbl(data = filtered, groups = .data$groups)
}
# Test verb in isolation
test_that("filter() basic case", { ... })
# Test verb preserves groups
test_that("filter() preserves groups", {
result <- my_tbl(df) |>
group_by(g) |>
filter(x > 1)
expect_equal(group_vars(result), "g")
})
# Test verb chains
test_that("filter + select + mutate pipeline works", {
result <- my_tbl(df) |>
filter(x > 0) |>
select(x, y) |>
mutate(z = x + 1) |>
collect
expect_namedresult
test_that
df data.framex y
dplyr_result df dplyrfilterx
my_result my_tbldf dplyrfilterx collect
expect_equalmy_result dplyr_result ignore_attr
Converted and distributed by TomeVault — claim your Tome and manage your conversions.