文字列と文字
Juliaでは文字列リテラルはダブルクォートで、文字リテラルはシングルクォートで書く。それぞれの型はString
とChar
だ。
この辺は、CやJavaなどと同じで、Pythonのように、どちらも文字列で適当に使い分ける、というようにはなっていない。
julia> typeof("test")
String
julia> typeof('t')
Char
String
もChar
も、Unicodeを表現する事ができる。ただし、Char
は個別のUnicodeポイントを表現しているのに対して、String
はUTF-8エンコードされたバイト列を格納している。String
がCharの配列ではないことに注意する必要がある。このあたりはRustも同じ。
文字列リテラルには任意のUnicode文字列がそのまま使える。関数length
でUnicode文字列としての長さを得ることができる。
julia> s = "てすと"
"てすと"
julia> length(s)
3
文字列の結合と繰り返し
文字列の結合は、まあどんな言語にも用意されていて、普通は+
演算子を使う。JavaもPythonもそうだ。が、Juliaではなぜか*
演算子を使う。
julia> "abc" + "def"
ERROR: MethodError: no method matching +(::String, ::String)
julia> "abc" * "def"
"abcdef"
文字列を複製して繰り返す演算もいくつかの言語に用意されているが、普通はこちらに*
を使う。Juliaではなんとべき乗の演算子である^
を使う。
julia> "abc" ^ 3
"abcabcabc"
なんでだ。どこから来たんだこの言語仕様。。。
Unicode文字列のインデックス
ここからがちょっと胡散臭い。文字列は配列のようにアクセスできるのだけど、その時のアクセスはバイト単位のインデックスとなる。
UTF-8エンコードは1文字が1バイトから4バイトまでの可変長表現なのでバイト単位でインデックスすると、そこが正しい文字境界である保証がない。
なので、エラーが起こる。。
julia> s[1]
'て': Unicode U+3066 (category Lo: Letter, other)
julia> s[2]
ERROR: StringIndexError("てすと", 2)
有効なインデックスを取得するにはnextindex
関数を用いる。最初と最後の有効なインデックスはそれぞれfistindex
, lastindex
で得られるので次のようにすると一文字ずつ取り出すことができる。面倒臭すぎる。
julia> function pick1by1(s)
index = firstindex(s)
while index <= lastindex(s)
println(s[index])
index = nextind(s, index)
end
end
pick1by1 (generic function with 1 method)
julia> pick1by1(s)
て
す
と
所感
文字列のUnicode対応はいろいろ面倒。かつてJavaはcharを16bitにしてみなを驚かせたが、その後Unicodeが16bitでは足りなくなって、身動き取れなくなっているようだ。どうすんだあれ。
一般に、Unicode文字列への対応方法としては 1) 1文字に3-4バイト割り当てて、文字の配列として保持する 2) UTF-8 などのエンコードにしてバイト列で保持する、の2択。
Python3は前者、Rust、Juliaは後者になっている。前者だとascii文字のみを格納する場合にデータ量が4倍になる。Rustのような性能命の言語ではこれは致命傷なので、後者を選択するのは妥当だと思うのだけど、Juliaのようなエンドユーザ向けの言語では、前者のほうが良かったんじゃないのかなあ。。