substr と substring

R の substr と substring は、どの引数がベクトル化されているかで異なる。前者は第一引数(対象の text) 、後者は位置(first, last 引数)がベクトル化されている。両方をベクトル化したものはないので、注意が必要だ。

> substr('ABCDEFG', 1:4, 4:7)
[1] "ABCD"
> substring('ABCDEFG', 1:4, 4:7)
[1] "ABCD" "BCDE" "CDEF" "DEFG"
> substr(c('ABCDEFG', '1234567'), 1, 4)
[1] "ABCD" "1234"
> substring(c('ABCDEFG', '1234567'), 1, 4)
[1] "ABCD" "1234"

# 両方をベクトル化しようとすると、おかしなことに!
> substr(c('ABCDEFG', '1234567'), 1:4, 4:7)
[1] "ABCD" "2345"
> substring(c('ABCDEFG', '1234567'), 1:4, 4:7)
[1] "ABCD" "2345" "CDEF" "4567"

ところで、substring の実装を見ると、

> substring
function (text, first, last = 1000000L) 
{
    if (!is.character(text)) 
        text <- as.character(text)
    n <- max(lt <- length(text), length(first), length(last))
    if (lt && lt < n) 
        text <- rep_len(text, length.out = n)
    .Internal(substr(text, as.integer(first), as.integer(last)))
}

となっていて、内部的には substr を呼び出している。すなわち、text 引数を明示的にベクトル化しておけば、substr も位置をベクトル化できるということである。実際、

> substr('ABCDEFG', 1:4, 4:7)
[1] "ABCD"
> substr(rep('ABCDEFG', 4), 1:4, 4:7)
[1] "ABCD" "BCDE" "CDEF" "DEFG"

となる。

しかしこの実装、文字列がゲノム配列などですごく長いときに、恐ろしく無駄な気がするのだが…… 普通に引数をリサイクルする仕様にしてほしかったな。