Unicode hacks by CL

Aug 17, 2014 12:35

Вот чего в CL однозначно очень круто -- так это отличная поддержка юникода. Одно то, что с юникодом в CL (на примере sbcl) нет ваще никаких проблем (нативные строки, исходники, репл, ввод/вывод, свободная конвертация utf-8/16/32/le/be), уже ставит его на голову выше любого другого языка, с которым мне доводилось работать более-менее продолжительное время. Навскидку: c/c++, java, perl, python, erlang -- везде свои засады, где-то большие, где-то мелкие.

Но есть в КЛ одна любопытная фича с сабжем, которую я нигде больше не видел: встроенная юникодная таблица имён символов:

CL-USER> (coerce "ёлкой" 'list)
(#\CYRILLIC_SMALL_LETTER_IO #\CYRILLIC_SMALL_LETTER_EL
#\CYRILLIC_SMALL_LETTER_KA #\CYRILLIC_SMALL_LETTER_O
#\CYRILLIC_SMALL_LETTER_SHORT_I)
Прикольно, что можно прямо в языке использовать символы по их имени:

CL-USER> (princ #\CYRILLIC_SMALL_LETTER_IO)
ё
#\CYRILLIC_SMALL_LETTER_IO
CL-USER> (remove #\CYRILLIC_SMALL_LETTER_IO "ещё")
"ещ"
CL-USER> (find #\CYRILLIC_SMALL_LETTER_SHCHA "ещё")
#\CYRILLIC_SMALL_LETTER_SHCHA
Ещё круче, что можно свободно получать имена символов строкой и даже рендерить их обратно, по строковому имени:

CL-USER> (map 'list #'char-name "ёж")
("CYRILLIC_SMALL_LETTER_IO" "CYRILLIC_SMALL_LETTER_ZHE")
CL-USER> (map 'string #'name-char *)
"ёж"
Это позволяет, например, делать вот такой ловкий хак:

CL-USER> (defun string-map-hack (regex-pattern regex-replacement)
(lambda (string)
(map 'string
(lambda (char) (name-char (cl-ppcre:regex-replace regex-pattern (char-name char) regex-replacement)))
string)))
STRING-MAP-HACK
CL-USER> (funcall (string-map-hack "_SMALL_" "_CAPITAL_")
"Строка")
"СТРОКА"
Понятно, конечно, что конкретно у данного примера нулевая практичность (при живой-то string-upcase), но ведь есть и более полезные применения этого хака. Например, вот так можно быстро срезать диакритику во вьетнамском тексте:

CL-USER> (coerce "thường" 'list)
(#\t #\h #\LATIN_SMALL_LETTER_U_WITH_HORN
#\LATIN_SMALL_LETTER_O_WITH_HORN_AND_GRAVE #\n #\g)
CL-USER> (funcall (string-map-hack "_WITH_.+" "")
"thường")
"thuong"
Или вот так быстро прикинуть, текст скорее русский или скорее английский:

CL-USER> (defun latin-or-cyrillic (string)
(flet ((count-these-chars (type) (count-if (lambda (char) (search type (char-name char))) string)))
(if (> (count-these-chars "LATIN") (count-these-chars "CYRILLIC"))
'latin
'cyrillic)))
LATIN-OR-CYRILLIC
CL-USER> (latin-or-cyrillic "в mc donald's кормят говном")
CYRILLIC
CL-USER> (latin-or-cyrillic "купил вчера Amon Amarth: Twilight of the Thunder God")
LATIN

code, unicode, utf-8, hack, lisp, common lisp, sbcl

Previous post Next post
Up