トラック
/
Julia
Julia
/
演習
/
ピカピカ
ピカピカ

ピカピカ

学習演習

はじめに

これは_大きな_テーマです! それについて長い本を一冊書くこともできますし、実際に何人もの人が書いています(Amazonで「unicode book」を検索すると、いくつか例が見られます)。

ごく簡単な歴史

何十年か前、プログラマーが英語だけが重要な言語だと思っていたころは、コンピューターで文字を扱うのはもっとずっと簡単でした。 つまり、英字26文字(大文字と小文字)、数字10個、いくつかの句読点、それにベルを鳴らすためのコード(0x07)で、これらすべてが7ビットに収まりました。これがASCII文字セットです。

当然のことながら、人々はàやä、Łはどうするのかと尋ね始め、次には別の人たちがऄやஹ、ญについて尋ね始め、若い人たちは絵文字😱を求めるようになりました。 どうすればよいのでしょうか?

手短に言うと、多くの賢く忍耐強い人たちが何年も委員会に参加して、Unicode文字セットやUTF-8などのエンコーディングの詳細を詰めなければならず、たくさんのソフトウェアが_非常に_複雑な書き直しを必要としました。 さらに、たくさんの新しいバグも生まれました。

すべて_が壊れてしまうのを防ぐため、Unicode/UTF-8の設計では、最初の127個のコードがASCIIとまったく同じになるようにしています(ベルまで含めて)_。

Juliaの文字

2005年以降に設計された言語には、ほどよく安定したUnicode標準がすでに存在していたという大きな利点があります。

Julia(2012年に最初のリリース)は、文字、文字列、変数名や関数名、数学の演算子など、すべてがUnicodeであると想定できました。

マニュアルを引用すると、「Juliaは、単なるASCIIテキストの扱いをシンプルかつ効率的にし、Unicodeの扱いも可能な限りシンプルかつ効率的にします」となります。 「可能な限り」という部分に注目してください。これはこの記述の重要なポイントです。

文字リテラルは一重引用符で書き、二重引用符で書く文字列とは別のものです。

C/C++の世界の人には当たり前ですが、PythonやJavaScriptのプログラマーには戸惑うところかもしれません。

julia> a = 'a'  # Roman alphabet
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)

julia> typeof(a)
Char

julia> jha = 'झ'  # Devanagari alphabet
'झ': Unicode U+091D (category Lo: Letter, other)

julia> typeof(jha)
Char

julia> '❤'  # heart emoji
'❤': Unicode U+2764 (category So: Symbol, other)

例からわかるように、型はCharで、Juliaは文字のカテゴリーについての詳しい情報も持っています。

よく見てみると、これらの文字は16進数4桁で表せるようです。 文字セット全体となると、最大で16進数6桁が必要です。

これらの数値は「コードポイント」と呼ばれ、現在はU+0000からU+10FFFFまでの範囲にあります。 REPLには表示されますが、コードの中ではcodepoints()を使って取得します。

CharとIntの間の変換は簡単です。

julia> Int('a')
97

julia> Char(97)
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)

コンパイラーは、Charに対する_一部の_整数演算を許可しています。

julia> 'b' - 'a'  # interval, in alphabetic order
1

julia> 'b' + 'a'
ERROR: MethodError: no method matching +(::Char, ::Char)

julia> 'a' + 5
'f': ASCII/Unicode U+0066 (category Ll: Letter, lowercase)

julia> 'f' + ('A' - 'a')  # same as `uppercase('f')`
'F': ASCII/Unicode U+0046 (category Lu: Letter, uppercase)

文字を扱う関数

文字列を扱う多くの関数は、Charの入力にも対応しています。

  • 対応する文字体系であれば、uppercase()とlowercase()で大文字・小文字を変換できます。
  • 大文字・小文字の判定にはisuppercase()とislowercase()を使います。
  • 文字の種類を調べるには、次のものを使います。
    • isletter()は多くの文字体系をカバーします
    • isdigit()は厳密に0:9のみを調べます
    • isnumeric()はisdigitより広く、¾やヨーロッパ以外のさまざまな文字体系でもtrueになります
    • isxdigit()は16進数の数字
    • isascii()はUnicode以前の文字
    • ispunct()は句読点
    • isspace()はあらゆる空白文字
    • isprint()は表示可能な文字(逆はiscntrl()です)
islowercase('A')  # false
uppercase('γ')  # 'Γ': Unicode U+0393 (category Lu: Letter, uppercase)
ispunct('@')  # true
isdigit('A')  # false
isxdigit('A')  # true

CharのベクターとStringの相互変換

StringからCharのベクターへは、collect()を使います。

CharのベクターからStringへは、String()コンストラクターがあります。

julia> s = "abcde"
"abcde"

julia> cv = collect(s)
5-element Vector{Char}:
 'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)
 'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase)
 'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase)
 'd': ASCII/Unicode U+0064 (category Ll: Letter, lowercase)
 'e': ASCII/Unicode U+0065 (category Ll: Letter, lowercase)

julia> String(cv)
"abcde"

これはASCIIだけでなく、どんな文字でも機能します。

julia> collect("❤,😱")
3-element Vector{Char}:
 '❤': Unicode U+2764 (category So: Symbol, other)
 ',': ASCII/Unicode U+002C (category Po: Punctuation, other)
 '😱': Unicode U+1F631 (category So: Symbol, other)

String()コンストラクターはベクターに対して働くことに注意してください。 1つのCharを1文字の文字列に_キャスト_するには、小文字のsを使ったstring()という関数を使います。

julia> string('a')
"a"

メモリ上の格納

ここまでの内容はどれも比較的シンプルに見えるので、本当に心配することはほとんどないのでしょうか?

残念ながら、これは楽観的すぎます!

1つの問題は、コードポイントごとに「最大」16進数6桁が必要だという点から来ています。 つまり、UTF-8でエンコードすると、文字によってメモリ上で必要な容量が異なります。

1バイトは(符号なしの)数値を255まで、つまり16進数2桁までしか格納できないので、UTF-8はCharを格納するのに可変のバイト数(1〜4)を使います。 これらは「コードユニット」と呼ばれ、ncodeunits()関数はある文字に必要な数を返します。

julia> codepoint(jha)  # jha 'झ' is defined in an earlier example
0x0000091d

julia> ncodeunits(jha)
3

julia> ncodeunits('a')  # ASCII character
1

julia> ncodeunits('😱')  # emoji
4

また、画面に表示できるものがすべて固有のコードポイントを持つわけでもありません。 見た目が異なる文字の中には、他の文字から派生したものと見なされるものがあり、Unicodeでは親となる文字と修飾子の組み合わせとして扱われます。

この問題は文字列にも影響し、インデックス付けに難しさをもたらします。

説明

この演習では、エンジニアが識別子の名前を整えるのを助けるための、ユーティリティルーチンの一部を実装します。

6つのタスクを通して、1文字を変換するtransformと、文字列を変換するcleanという関数を少しずつ作り上げていきます。

有効な識別子は、0個以上の文字、アンダースコア、ハイフン、疑問符、絵文字で構成されます。

clean関数に空文字列を渡した場合は、空文字列が返されるようにします。

1. ハイフンをアンダースコアに置き換える

transform関数を実装して、ハイフンをアンダースコアに置き換えます。

julia> transform('-')
"_"

2. 空白をすべて削除する

空白文字をすべて削除します。 先頭と末尾の空白も含みます。

julia> transform(' ')
""

3. camelCaseをkebab-caseに変換する

transform関数を変更して、camelCaseをkebab-caseに変換します。

julia> transform('D')
"-d"

4. 数字の文字を除外する

transform関数を変更して、数値である文字を除外します。

julia> transform('7')
""

5. ギリシャ文字の小文字を疑問符に置き換える

transform関数を変更して、'α'から'ω'の範囲のギリシャ文字を置き換えます。

julia> transform('β')
"?"

6. これらの操作を組み合わせて文字列を処理する

clean関数を実装して、これらの操作を文字列全体に適用します。

ルールに当てはまらない文字は、そのまま変更せずに通します。

julia> clean("  a2b Cd-ω😀  ")
"ab-cd_?😀"
GitHubで編集する リンクは新しいウィンドウまたはタブで開きます
Julia Exercism

ピカピカを始める準備はできましたか?

Exercismに登録すれば、35個のコンセプト128個の演習、そして本物の人間によるメンタリングとともに、Juliaを学んでマスターできます。すべて無料です。