== и% in% отличаются от кодировки символов?

Мне трудно понять, почему == и %in% будут давать разные результаты при применении к векторам символов, которые зависят, по-видимому, только от кодирования векторов. Пример:

a <- 'Köln'
Encoding(a) <- 'unknown'
Encoding(a)
# [1] "unknown"

b <- a
Encoding(b) <- 'UTF-8'

a == b
# [1] TRUE
a %in% b
# [1] FALSE

Обновление

Похоже, что результат также зависит от платформы. Возвращаются два оператора:

  • TRUE и FALSE на R 3.3.0 на OS X 10.11.5
  • FALSE и FALSE на R 3.3.0 на Windows 10 (64 бит)
  • TRUE и TRUE на R 3.2.3 на CentOS 7

Я начинаю думать, что это ошибка.

Ответы

Ответ 1

Это действительно ошибка, и она была исправлена ​​в 3.3.1.

Поведение на самом деле немного страннее, чем показывает ваш пример, поскольку вы получаете только FALSE, когда у вас есть один элемент в левой части %in%:

> a %in% b
[1] FALSE
> c(a, a) %in% b
[1] TRUE TRUE

Как видно из комментариев, %in% просто вызывает match, поэтому проблему можно увидеть и там:

> match(a, b)
[1] NA
> match(c(a, a), b)
[1] 1 1

Важными аргументами %in% и match являются x и table, где любая функция ищет x в table. Под капотом R делает это в функции match5, определенной в unique.c. В случае, если у вас более одного x, match5 создаст хэш-таблицу из table, чтобы включить быстрый поиск. Если вы копаете код, вы увидите, что сравнение выполняется в функции с именем sequal, которая возвращает Seql(STRING_ELT(x, i), STRING_ELT(y, j)) (ну, это на самом деле немного сложнее, чем это *). Затем, если вы посмотрите Seql в memory.c, вы найдете:

int result = !strcmp(translateCharUTF8(a), translateCharUTF8(b));

Что, как вы можете видеть, преобразует строки в UTF-8.

Однако, если x имеет только один элемент, глупо идти на проблему создания хеш-таблицы, так как мы можем просто сканировать через table один раз, чтобы увидеть, есть ли x. В 3.3.0 код для проверки равенства между x и каждым элементом table не использовал Seql и не преобразовал строку в UTF-8. Но начиная с 3.3.1 используется Seql, поэтому поведение исправлено.

* Немного в стороне от равенства строк: R фактически кэширует строки, чтобы не было необходимости хранить кучу копий. Поэтому, если две строки находятся в одном и том же месте, они равны, и нет необходимости проверять далее!

> .Internal(inspect("Köln"))
@10321b758 16 STRSXP g0c1 [NAM(2)] (len=1, tl=0)
  @106831eb8 09 CHARSXP g1c1 [MARK,gp=0x28,ATT] [UTF8] [cached] "Köln"
> .Internal(inspect(b))
@106831cd8 16 STRSXP g1c1 [MARK,NAM(2)] (len=1, tl=0)
  @106831eb8 09 CHARSXP g1c1 [MARK,gp=0x28,ATT] [UTF8] [cached] "Köln"