cjk_jamo() splits each Hangul syllable into the jamo it is built from;
cjk_compose_jamo() puts them back together.
Arguments
- x
A character vector. Anything else is coerced with
as.character(). That coercion is R's, not this package's, so a numeric vector is measured as R chooses to write it – which moves withoptions(scipen)andoptions(OutDec), and can therefore differ between sessions. Convert deliberately if you mean to measure numbers; these verbs are for text. A list is not coerced –as.character()deparses one rather than coercing it, so the text measured would be the R code that builds the list – so a list, a data frame or a function is an error naming what to do instead.
Value
cjk_jamo() returns a list the same length as x, each element a
character vector of jamo and of the other characters, one per code
point; NA gives NA_character_ and the empty string gives
character(0). cjk_compose_jamo() takes a character vector and
returns one.
Details
A modern Hangul syllable is a composite. Unicode encodes 11,172 of them
precomposed in the Hangul Syllables block, each one algorithmically derived
from a leading consonant, a vowel and an optional trailing consonant:
SIndex = (LIndex * 21 + VIndex) * 28 + TIndex. Both verbs apply that
arithmetic directly, so the decomposition is exact: no table can be out of
date and no syllable is missed.
Only Hangul is touched
A character that is not a Hangul syllable passes through cjk_jamo() as
itself, and cjk_compose_jamo() joins conjoining jamo into syllables and
changes nothing else, so both are safe to run over a mixed column. That is
deliberately narrower than Unicode normalisation. NFD would also split an
accented Latin letter, and NFD and NFC alike replace a CJK compatibility
ideograph with its unified form, which in a Korean column means silently
rewriting Hanja: the compatibility ideographs exist so that the Korean
legacy encodings round-trip. Use cjk_normalize() when normalisation is
what you want.
The round trip
cjk_jamo() returns a list, one character vector of jamo per element
of x, and cjk_compose_jamo() takes a character vector, so the two do
not compose directly: join each element first, as the rt() helper in the
examples does. Writing cjk_compose_jamo(cjk_jamo(x)) is an error rather
than a silent wrong answer, because as.character() would deparse the list
and hand back the literal string c("\u1112", "\u1161", "\u11ab").
Joined and composed, the jamo give back x exactly, with one exception
that is the point of cjk_compose_jamo(): conjoining jamo that x
already held as separate code points come back composed into their
syllable.
That makes jamo the right unit for questions the syllable hides: which
initial consonants a corpus favours, whether two spellings differ only in
a final consonant, or how to sort by consonant. U+D55C counts three ways,
each right for a different question: one character to nchar(), two
terminal columns to cjk_width() (a Hangul syllable is East Asian Wide),
and three jamo here.
See also
cjk_script() to detect Hangul, cjk_blocks() for the blocks
involved, cjk_normalize() for the Unicode normalisation forms.
Examples
# U+D55C U+AE00, "Hangul"
cjk_jamo("\ud55c\uae00")
#> [[1]]
#> [1] "ᄒ" "ᅡ" "ᆫ" "ᄀ" "ᅳ" "ᆯ"
#>
# join each element and compose it: the round trip is exact
rt <- function(x) {
cjk_compose_jamo(vapply(cjk_jamo(x), paste, character(1), collapse = ""))
}
rt("\ud55c\uae00")
#> [1] "한글"
# text that is not Hangul is left alone: the accent stays where it was,
# and the compatibility ideograph U+F900 is not swapped for U+8C48
cjk_jamo("e\u0301")
#> [[1]]
#> [1] "e" "́"
#>
rt("\uf900") == "\uf900"
#> [1] TRUE
