r-oop について
S7、S3、S4、vctrs の選択を支援し、クラス、メソッド、継承、移行のパターンを示す R 開発者向けスキルです。R オブジェクトの設計や既存コードの移行検討に役立ちます。
主な機能
- 01S7・S3・S4・vctrs の比較
- 02オブジェクトの種類に応じた選択
- 03クラスとメソッドの実装パターン
- 04継承と多重ディスパッチの説明
- 05システム移行の指針
活用シーン
- 01新規プロジェクトの OOP システム選定
- 02ベクトル型のカスタムクラス作成
- 03複雑なオブジェクトへの検証追加
- 04S3 または S4 の移行計画
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ ab604/ claude-code-r-skills --skill r-oopインストール後、Claude Code で/r-oopを入力して読み込まれたか確認します。
詳細
R のオブジェクト指向プログラミング
S7、S3、S4、vctrs:ニーズに合った OOP システムの選び方
S7:新規プロジェクト向けの最新 OOP
- S7 は S3 のシンプルさと S4 の構造を兼ね備えています
- 自動検証機能を備えた正式なクラス定義
- 既存の S3 コードとの互換性
# S7 class definition
Range <- new_class("Range",
properties = list(
start = class_double,
end = class_double
),
validator = function(self) {
if (self@end < self@start) {
"@end must be >= @start"
}
}
)
# Usage - constructor and property access
x <- Range(start = 1, end = 10)
x@start # 1
x@end <- 20 # automatic validation
# Methods
inside <- new_generic("inside", "x")
method(inside, Range) <- function(x, y) {
y >= x@start & y <= x@end
}
OOP システム選択マトリックス
S7、vctrs、S3/S4 の選択フローチャート
ここから始めます: 何を構築しますか?
1. ベクトルのようなオブジェクト(アトミックベクトルのように振る舞うもの)
Use vctrs when:
- Need data frame integration (columns/rows)
- Want type-stable vector operations
- Building factor-like, date-like, or numeric-like classes
- Need consistent coercion/casting behavior
- Working with existing tidyverse infrastructure
Examples: custom date classes, units, categorical data
2. 一般的なオブジェクト(複雑なデータ構造で、ベクトルのような振る舞いをしないもの)
Use S7 when:
- NEW projects that need formal classes
- Want property validation and safe property access (@)
- Need multiple dispatch (beyond S3's double dispatch)
- Converting from S3 and want better structure
- Building class hierarchies with inheritance
- Want better error messages and discoverability
Use S3 when:
- Simple classes with minimal structure needs
- Maximum compatibility and minimal dependencies
- Quick prototyping or internal classes
- Contributing to existing S3-based ecosystems
- Performance is absolutely critical (minimal overhead)
Use S4 when:
- Working in Bioconductor ecosystem
- Need complex multiple inheritance (S7 doesn't support this)
- Existing S4 codebase that works well
S7 と S3 の詳細な比較
| 機能 | S3 | S7 | S7 が適している場合 |
|---|---|---|---|
| クラス定義 | 非形式的(慣習に基づく) | 形式的(new_class()) | 構造の保証が必要 |
| プロパティへのアクセス | $ または attr()(安全ではない) | @(安全で検証済み) | プロパティの検証が重要 |
| 検証 | 手動で、一貫性がない | 組み込みのバリデーター | データの整合性が重要 |
| メソッドの検索 | メソッドを見つけにくい | メソッドを明確に表示 | 開発者の使いやすさが重要 |
| 多重ディスパッチ | 限定的(base のジェネリック) | 完全な多重ディスパッチ | 複雑なメソッドのディスパッチが必要 |
| 継承 | 非形式的、NextMethod() | 明示的な super() | 予測可能な継承が必要 |
| 移行コスト | - | 低い(1~2 時間) | より優れた構造が必要 |
| パフォーマンス | 最速 | S3 とほぼ同等 | パフォーマンスの差は無視できる |
| 互換性 | S3 と完全互換 | S3 と完全互換 + S7 | 古いパターンと新しいパターンの両方が必要 |
実践的なガイドライン
次のような場合は S7 を選びます
# Complex validation needs
Range <- new_class("Range",
properties = list(start = class_double, end = class_double),
validator = function(self) {
if (self@end < self@start) "@end must be >= @start"
}
)
# Multiple dispatch needs
method(generic, list(ClassA, ClassB)) <- function(x, y) ...
# Class hierarchies with clear inheritance
Child <- new_class("Child", parent = Parent)
次のような場合は vctrs を選びます
# Vector-like behavior in data frames
percent <- new_vctr(0.5, class = "percentage")
data.frame(x = 1:3, pct = percent(c(0.1, 0.2, 0.3))) # works seamlessly
# Type-stable operations
vec_c(percent(0.1), percent(0.2)) # predictable behavior
vec_cast(0.5, percent()) # explicit, safe casting
次のような場合は S3 を選びます
# Simple classes without complex needs
new_simple <- function(x) structure(x, class = "simple")
print.simple <- function(x, ...) cat("Simple:", x)
# Maximum performance needs (rare)
# Existing S3 ecosystem contributions
S3 のパターン
基本的な S3 クラス
# Constructor
new_person <- function(name, age) {
stopifnot(is.character(name), length(name) == 1)
stopifnot(is.numeric(age), length(age) == 1)
structure(
list(name = name, age = age),
class = "person"
)
}
# Print method
print.person <- function(x, ...) {
cat("Person:", x$name, "(age", x$age, ")\n")
invisible(x)
}
# Generic + method
greet <- function(x) UseMethod("greet")
greet.person <- function(x) {
cat("Hello, my name is", x$name, "\n")
}
greet.default <- function(x) {
cat("Hello!\n")
}
S3 の継承
# Child class
new_employee <- function(name, age, company) {
obj <- new_person(name, age)
obj$company <- company
class(obj) <- c("employee", class(obj))
obj
}
# Method with inheritance
print.employee <- function(x, ...) {
NextMethod() # Call parent print method
cat("Works at:", x$company, "\n")
invisible(x)
}
S7 のパターン
基本的な S7 クラス
library(S7)
# Define class
Person <- new_class("Person",
properties = list(
name = class_character,
age = class_numeric
),
validator = function(self) {
if (self@age < 0) {
"@age must be non-negative"
}
}
)
# Create instance
bob <- Person(name = "Bob", age = 30)
bob@name # "Bob"
bob@age <- 31 # Validated assignment
S7 のメソッド
# Define generic
greet <- new_generic("greet", "x")
# Add method
method(greet, Person) <- function(x) {
cat("Hello, my name is", x@name, "\n")
}
# Default method
method(greet, class_any) <- function(x) {
cat("Hello!\n")
}
S7 の継承
Employee <- new_class("Employee",
parent = Person,
properties = list(
company = class_character
)
)
# Override method
method(greet, Employee) <- function(x) {
super(x, Person)@greet() # Call parent method
cat("I work at", x@company, "\n")
}
S7 の多重ディスパッチ
# Generic with multiple dispatch
combine <- new_generic("combine", c("x", "y"))
# Method for specific combination
method(combine, list(Person, Person)) <- function(x, y) {
cat(x@name, "meets", y@name, "\n")
}
method(combine, list(Person, class_character)) <- function(x, y) {
cat(x@name, "receives message:", y, "\n")
}
移行戦略
- S3 -> S7:通常は 1~2 時間の作業で、完全な互換性を維持できます
- S4 -> S7:より複雑です。S4 の機能が実際に必要かどうかを評価してください
- Base R -> vctrs:ベクトルのようなクラスでは、大きなメリットがあります
- アプローチの組み合わせ:S7 クラスでは、内部で vctrs の原則を使用できます
移行例:S3 から S7 へ
# Original S3
new_person_s3 <- function(name, age) {
structure(list(name = name, age = age), class = "person")
}
# Migrated S7
Person <- new_class("Person",
properties = list(
name = class_character,
age = class_numeric
)
)
# S7 is backwards compatible with S3 generics
# Existing S3 methods still work
OOP を使わないほうがよい場合
よりシンプルなアプローチのほうが適していることもあります:
# Don't create a class for simple data
# BAD
Point <- new_class("Point", properties = list(x = class_double, y = class_double))
# GOOD - just use a named list or vector
point <- c(x = 1.5, y = 2.3)
# Don't create classes for one-off operations
# Use functions instead
distance <- function(p1, p2) {
sqrt((p1["x"] - p2["x"])^2 + (p1["y"] - p2["y"])^2)
}
コメント
Markdown 対応