Создайте последовательность между двумя буквами

Я хочу создать последовательность между двумя буквами, скажем, "b" и "f". Таким образом, выход

"b" "c" "d" "e" "f"

Для чисел мы можем сделать

2:6 #which gives output as 
[1] 2 3 4 5 6

Есть ли простой способ сделать это с помощью писем?

Я прошел Создать последовательность символов из 'A' - 'Z', но это производит все буквы, а не последовательность между конкретными буквами.

Мое текущее решение:

indx <- which(letters %in% c("b", "f")); 
letters[indx[1] : indx[2]]

#[1] "b" "c" "d" "e" "f"

Это работает, но мне любопытно, есть ли простой способ сделать это или функцию в любом из пакетов, которые я пропустил?

Примечание: мне не нужны letters[2:6] как я не знаю 2 и 6 заранее. Это может быть между любыми двумя буквами.

Ответы

Ответ 1

Это будет еще один базовый вариант R:

letters[(letters >= "b") & (letters <= "f")]
# [1] "b" "c" "d" "e" "f"

Ответ 2

Вы можете создать свою собственную функцию:

'%:%' <- function(l, r) {
    intToUtf8(seq(utf8ToInt(l), utf8ToInt(r)), multiple = TRUE)
}

Использование:

"b" %:% "f"
# [1] "b" "c" "d" "e" "f"

"f" %:% "b"
# [1] "f" "e" "d" "c" "b"

"A" %:% "D"
# [1] "A" "B" "C" "D"

Ответ 3

Другой вариант с match, seq и do.call:

letters[do.call(seq, as.list(match(c("b","f"), letters)))]

который дает:

[1] "b" "c" "d" "e" "f"

Создание такой функции, чтобы она работала как с прописными, так и с прописными буквами:

char_seq <- function(lets) {
  switch(all(grepl("[[:upper:]]", lets)) + 1L,
         letters[do.call(seq, as.list(match(lets, letters)))],
         LETTERS[do.call(seq, as.list(match(lets, LETTERS)))])
}

вывод этого:

> char_seq(c("b","f"))
[1] "b" "c" "d" "e" "f"

> char_seq(c("B","F"))
[1] "B" "C" "D" "E" "F"

Эта функция может быть расширена за счет проверки правильности ввода:

char_seq <- function(lets) {
  g <- grepl("[[:upper:]]", lets)
  if(length(g) != 2) stop("Input is not of length 2")
  if(sum(g) == 1) stop("Input does not have all lower-case or all upper-case letters")
  switch(all(g) + 1L,
         letters[do.call(seq, as.list(match(lets, letters)))],
         LETTERS[do.call(seq, as.list(match(lets, LETTERS)))])
}

приводя к правильным сообщениям об ошибках, когда ввод неправильный:

> char_seq(c("B"))
Error in char_seq(c("B")) : Input is not of length 2

> char_seq(c("b","F"))
Error in char_seq(c("b", "F")) : 
  Input does not have all lower-case or all upper-case letters

Ответ 4

Играя с UTF, что-то вроде:

intToUtf8(utf8ToInt("b"):utf8ToInt("f"), multiple = TRUE)
# [1] "b" "c" "d" "e" "f"

Ответ 5

Возможно, использование необработанных версий букв, а затем преобразование обратно в символ можно использовать для определения инфиксной функции, аналогичной ":"

 '%c:%' <- function(x,y) { strsplit( rawToChar(as.raw(
     seq(as.numeric(charToRaw(x)), as.numeric(charToRaw(y))))), "" )[[1]]}
>  'a' %c:% 'g'
[1] "a" "b" "c" "d" "e" "f" "g"

Я, конечно, не утверждаю, что это удовлетворяет просьбе о "простом способе сделать это", и я даже не уверен, что это будет более эффективным, но он действительно вводит пару потенциально полезных функций.

Ответ 6

Почему бы и нет?

letters[which(letters == 'b') : which(letters == 'f')]

Ответ 7

Я знаю, что это осуждается, но вот решение eval(parse(...))

LETTERS[eval(parse(text = paste(which(LETTERS %in% c('B', 'F')), collapse = ':')))]
#[1] "B" "C" "D" "E" "F"

Ответ 8

Перво-наперво: ваш код

which(letters %in% c("b", "f"))

Это правильный, но запутанный способ написания

match(c('b', 'f'), letters)

(Почему "извилистый"? Потому что %in% является оберткой для match для конкретного варианта использования, который явно превращает числовой индекс в логическое значение, то есть обратную операцию which.)

Затем вы, конечно, можете использовать результат и преобразовать его в диапазон с помощью idx[1L]: idx[2L] и в этом нет ничего плохого. Но у R есть идиоматический способ выражения концепции вызова функции с использованием вектора в качестве ее параметров: do.call:

do.call(':', as.list(match(c('b', 'f'), letters)))

Или, что эквивалентно:

do.call(seq, as.list(match(c('b', 'f'), letters)))

{purrr} позволяет нам делать то же самое без as.list:

purrr::invoke(seq, match(c('b', 'f'), letters))

И, наконец, мы подмножество:

letters[purrr::invoke(seq, match(c('b', 'f'), letters))]