Julia/Strings
文字列の型はStringである
Stringは、UTF-8によってUnicodeをサポートしている
Stringを含むすべての文字列の型はAbstractStringのサブタイプである
異なるエンコーディングなどによる「文字列」の仕組みをサポートするため
Juliaは、文字列のためにChar型をサポートする
String同様に、AbstractCharがある
32ビット整数でUTF-8ベースの表現を行う
文字列はイミュータブルである
文字列は、インデックスから文字への関数とみなすことができる
code:jl
'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase)
Char
内部では32ビットの表現になっているプリミティブ
Unicodeのコードポイントと相互に変換可能
リテラルはシングルクオートを使って'有'である
Scalaと同じ
Int(char)で文字をInt64のコードポイントに写し、Char(codepoint)でコードポイントから文字に写せる
code:jl
julia> Char(Int('有')) == '有'
true
パフォーマンスのため、Charは有効なコードポイントかを確認しない
かわりにisvalid(char)で検査可能
code:jl
julia> Char(2000000)
'\U1e8480': Unicode U+1E8480 (category In: Invalid, too high)
julia> isvalid(Char(2000000))
false
任意のUnicodeリテラルを直接記述してよい
code:jl
julia> '\u2200'
'∀': Unicode U+2200 (category Sm: Symbol, math)
比較が可能
code:jl
julia> 'a' < '∀'
true
少しの計算も可能
code:jl
julia> '有' + 4
'服': Unicode U+670D (category Lo: Letter, other)
String
文字列のリテラルはダブルクオートを使って"this is a string"である
インデックスアクセスが可能
code:jl
julia> "hello, world"begin 'h': ASCII/Unicode U+0068 (category Ll: Letter, lowercase)
'o': ASCII/Unicode U+006F (category Ll: Letter, lowercase)
'd': ASCII/Unicode U+0064 (category Ll: Letter, lowercase)
',': ASCII/Unicode U+002C (category Po: Punctuation, other)
インデックスアクセスで利用できる特殊なトークン: begin(== 1)、end
関数だとfirstindex(sth) / lastindex(sth) がある
range indexingもできる
code:jl
"4567"
インデキシングするとコピーが作られるが、SubString("looooooooooong", 2:5)すると同じ文字列のビューとして部分文字列を作れる
メモリを共有するので環境に優しい
関数によってはこれを返す
code:jl
julia> strip(" oops ")
"oops"
julia> typeof(ans)
SubString{String}
文字列のインデキシングはbyte単位である
code:jl
ERROR: StringIndexError: invalid index 2, valid nearby indices 1=>'全', 4=>'裸' Stacktrace:
1 string_index_err(s::String, i::Int64) @ Base ./strings/string.jl:12
2 getindex_continued(s::String, i::Int64, u::UInt32) @ Base ./strings/string.jl:472
3 getindex(s::String, i::Int64) @ Base ./strings/string.jl:464
コードポイントの先頭インデックスを得る関数がある
code:jl
julia> s = "全裸"
"全裸"
julia> nextind(s, 1)
4
julia> prevind(s, 4)
1
UTF-8は1文字の大きさが可変長なのでこうなってしまう
lengthはコードポイント単位で数えてくれる
code:jl
julia> lastindex("🍣🍣🍣")
9
julia> length("🍣🍣🍣")
3
for ... inでは正しくハンドルしてくれる
code:jl
julia> for c in "🍣🍣🍣" println(c) end
🍣
🍣
🍣
直接インデックスを得ることもできる
code:jl
julia> collect(eachindex("🍣🍣🍣"))
3-element Vector{Int64}:
1
5
9
codeunitsでUTF-8をバラせる
code:jl
julia> codeunits("🍣🍣🍣")
12-element Base.CodeUnits{UInt8, String}:
0xf0
0x9f
0x8d
0xa3
0xf0
0x9f
0x8d
0xa3
0xf0
0x9f
0x8d
0xa3
結合
string("foo", "bar")
"foo" * "bar"も等価
他の多くの言語とは違い、+ではなく*を使う
数学だと普通、$ +といったら可換な演算にしか使わない
ところが文字列の結合はそうじゃねえよなァ!?
というわけで*を使っているとのこと
自由モノイド$ \langle S, * \rangleの文脈もある
interpolation
Perlライクな記法がある
code:jl
julia> who = "windymelt"
"windymelt"
julia> "Hello, $(who)!"
"Hello, windymelt!"
()は曖昧さがなければ外していい
式を受けるので計算もできる
code:jl
julia> "10! = $(factorial(10))"
"10! = 3628800"
これはカッコがないと無理やな
AbstractString以外の値は適当な表現にされる
\$とすればエスケープできる
triple quote
"""で括ることで複数行を表現できる
"""の直後の改行は取り除かれる
行頭のスペースは取り除かれる
ただし、文字列のインデントが揃っている必要がある
code:jl
julia> """
ok
"""
"ok\n"
julia> """
ok"""
"ok"
julia> """
ng
"""
" ng\n"
行末にスペースがあっても取り除かれない
Scalaよりも結構アグレッシブだな
Scalaは改行を取り除かないし、スペースを取り除かない
code:scala
scala> """
| string"""
val res1: String = "
string"
code:jl
julia> """
string"""
"string"
triple quoteにおいて、改行は常にLFのみである
ファイルのエンコーディングやOSによらずLFに正規化される
辞書順比較が可能
code:jl
julia> "aaa" < "aab" < "aba"
true
occursinで部分文字列判定が可能
code:jl
julia> occursin("anana", "panamabanana")
true
repeat
code:jl
julia> "y" * repeat("o", 10)
"yoooooooooo"
Non-Standard String Literals
""の直前に文字列を前置することで特殊なリテラルを利用できる
これはマクロなので自作もできる
Scalaと同じ
例
r"\bin\b"
正規表現リテラル
Juliaはマルチディスパッチ言語なのでoccursinでマッチ確認できる
code:jl
julia> reg = r"\bin\b"
r"\bin\b"
julia> occursin(reg, "anarchy in the uk")
true
マッチはそのままmatch関数で確認できる
マッチしないときはnothing(Nothing型)という特殊な値を返す
code:jl
julia> m = match(reg, "anarchy in the uk")
RegexMatch("in")
julia> m.
captures
match
offset
offsets
regex
julia> m.match
"in"
julia> m.offset
9
julia> m.captures
Union{Nothing, SubString{String}}[]
マッチする場合はdestructuringできる
code:jl
julia> a, b = match(r"(\d\d\d)-(\d\d\d\d)", "123-4567")
RegexMatch("123-4567", 1="123", 2="4567")
julia> a
"123"
julia> b
"4567"
名前付きキャプチャもできる
code:jl
julia> m = match(r"(?<head>\d\d\d)-(?<tail>\d\d\d\d)", "123-4567")
RegexMatch("123-4567", head="123", tail="4567")
"123"
置換
code:jl
julia> replace("123-4567", r"(?<head>\d\d\d)-(?<tail>\d\d\d\d)" => s"\g<tail>-\g<head>")
"4567-123"
置換文字列はs""を使う
=> は Pairという型っぽい
code:jl
julia> sizeof(42)
8
julia> sizeof(42 => 42)
16
ismxそれぞれのmodifierが利用可能
r""リテラルでは変数展開は行なわれない(やりたかったらRegexで直接作る)
b"deadbeef"
バイナリリテラル
UInt8配列になる
v"1.2.3"
セマンティックバージョンリテラル
便利だけど標準ライブラリに入れるあたりに気概がある
raw"\\\\\/\///\\\/\" raw string literal
Annotated Strings
文字列の一部にメタデータを埋め込む
code:jl
"I am windymelt"
julia> Base.annotations(as)
1-element Vector{@NamedTuple{region::UnitRange{Int64}, label::Symbol, value}}:
@NamedTuple{region::UnitRange{Int64}, label::Symbol, value}((6:14, :type, :name))
julia> Base.annotations(as)1.region 6:14
julia> as[Base.annotations(as)1.region] "windymelt"
Emacsにも似た仕組みがあったなぁ