AliceScript 3.0の新機能
この記事では、AliceScript3.0の変更点と新機能について説明します
新機能
AliceScript 3.0(Alice3.0)は、Alice2.3の後継です。 Alice3.0では大きく分けて3つの領域で性能が向上しました。
- 開発効率の向上 : Alice3.0では、変数への型チェック・
null
チェック、関数での契約、名前空間を用いて変数や関数を整理できるようになりました。これによって、より複雑なコードでも可読性が向上します。 - 相互運用性の向上 : Alice3.0では、関数バインドや関数の外部実装などによって.NETやC言語製のAPIとシームレスに通信できるようになりました。これによって、よりたくさんのことをAliceScriptでこなせるようになります。
- パフォーマンスの向上 : AliceScriptとLosettaで、Alice2.3と比較して性能が最大で100倍向上しました。そのほかにも、さまざまなケースでパフォーマンスが飛躍的に向上しています。
最新のAliceScriptADKはAliceScriptのダウンロードからダウンロードできます。
ご意見をお寄せください
AliceScriptの新機能に関するご意見や不具合がある場合、AliceScriptのリポジトリにお寄せください。
変数関係
型チェックの導入
Alice3.0での変数は、自動的に型チェックされており、一度定まった型に異なる型の値を代入することはできません。また、変数は宣言時に型を宣言できます。 次の例をご覧ください。
型キーワードの代わりにvar
キーワードを使用することで、右辺の型を使用して自動的に変数の型を決定できます。次の例をご覧ください。
従来通りどんな型の値でも代入できる変数を作成するには、variable
キーワードを使用して、バリアント型を定義します。バリアント型の変数は型チェックが省略されます。
null
チェックの導入
型チェック同様、Alice3.0での変数は自動的にnull
チェックがされており、通常変数にnull
を代入することはできません。null
を代入できる変数を作成するには、型キーワードに?
を付けます。次の例をご覧ください。
関数関係
戻り値の型指定
関数のシグネチャに戻り値の型を記述することで、その関数がどんな値を返すかを明らかにできます。次の例をご覧ください。
string GetUserName()
{
return "UserName"; //文字列を返す
}
string BadFunc()
{
return 0; //戻り値の型が異なるためエラー
}
void SubRoutine()
{
return; //戻り値を返さない(暗黙的にvoidを返している)
}
また、関数の宣言時にfunction
キーワードが不要になりました。次の例でFunc1
とFunc2
、Func3
はどちらも同じ内容の関数です。
function Func1()
{
print("Hello,World");
}
void Func2()
{
print("Hello,World");
}
Func3()
{
print("Hello,World");
}
参照渡し
関数内で引数の値を変更し、その変更を呼び出し元にも反映できます。AliceScriptではref
キーワードを使用することで、参照渡しであることが明らかになり、間違いを防げます。
次に例を示します。
void Increment(ref number num)
{
num++;
}
var i = 0;
Increment(ref i);
print(i); //出力 : 2
拡張メソッド
任意の型について、拡張メソッドを実装できるようになりました。
また、拡張メソッドとしてのみ呼び出せるようにするextonly
キーワードを導入しました。
次に例を示します。
void Output(this string str)
{
print(str);
}
string s = "Hello,World";
Output(s); //通常の関数として呼び出し
s.Output(); //拡張メソッドとして呼び出し
extonly number Pow(this number a)
{
return a * a;
}
number n = 2;
n.Pow(); //拡張メソッドとして呼び出し
Pow(n); //これはエラー
この変更について詳しく知るには、Losetta#99を参照してください。
型を省略することで、すべての型で使用できる拡張メソッドを定義できます。 次の例をご覧ください。
この変更について詳しく知るには、Losetta#107を参照してください。
関数の外部実装
外部で実装された関数をAliceScriptで使用するには、extern
キーワードを使用して関数を宣言します。
次の例では、.NETで実装されているSystem.Console.WriteLineメソッドを使用してコンソールにメッセージを表示します。
#netimport "System.Console","System.Console"
extern void WriteLine(string text);
WriteLine("Hello,World");
また、次の例では、Win32APIで定義されているMessageBox関数を使用してメッセージを表示します。
#libimport "user32.dll"
extern int MessageBox(HWND hwnd,LPCSTR lpText,LPCTSTR lpCaption,UINT uType);
MessageBox(0,"Hello,World!","TestMessage",0);
関数と契約
requires
およびensure
キーワードを使用して関数の事前条件・事後条件を明記することで、関数がとりうる値を表明できます。次の例をご覧ください。
number Divide(number numerator, number denominator)
requires (denominator != 0) ensures (return >= 0) // 表明
{
return numerator / denominator;
}
この関数は、numerator
をdenominator
で除算します。
この関数を呼び出すとき、denominator
は0
以外の値である必要があり(requires
による事前条件の表明)、この関数は常に0
以上の値を返します(ensures
による事後条件の表明)。
この変更について詳しく知るには、Losetta#104を参照してください。
ユーザー定義の名前空間
名前空間を使用すると、たくさんの関数や関数を名前空間単位で分類できます。 次の例をご覧ください。
namespace MyNamespace
{
// この関数は名前空間の外からアクセスできる
public void PublicFunc()
{
print($"Hello,{Name}");
}
// この変数は名前空間の外からアクセスできる
public string Name = "John";
// この関数は同じ名前空間内からアクセスできる
internal void InternalFunc()
{
print($"Hello,{Name}");
}
// この関数はこのスコープのみからアクセスできる
private void PrivateFunc()
{
print($"Hello,{Name}");
}
}
MyNamespace.PublicFunc(); //呼び出し
print(MyNamespace.Name); //変数アクセス
namespace MyNamespace
{
InternalFunc(); //呼び出し
}
この変更について詳しく知るには、Losetta#101を参照してください。
文字列リテラル関係
UTF-8リテラル
文字列リテラルの先頭にu8
を追加することで、文字列をUTF-8バイナリの形式で表現できます。次の例をご覧ください。
文字列補間
文字列補間を使用すると、文字列を連結して結果を表示する代わりに文字列中で式の結果を使用できます。次の例をご覧ください。
エスケープ文字
新しいエスケープ文字\e
を使用すると、ANSI X.364を用いてコンソールでさまざまな効果を表現できます。次の例では、コンソールでテキストを赤い文字でを表示しています。
Unicodeエスケープシーケンス
Unicodeエスケープシーケンスを使用すると、任意のUnicode文字をコードポイントを使って表記できます。次の例をご覧ください。
// `N`のUnicodeコードポイントはU+0004e
print("\u004e"); //出力 : N (\uXXXX)4ケタ
print("\U0000004e"); //出力 : N (\Uxxxxxxxx)8ケタ
print("\x4e"); //出力 : N (\xXX)1~4ケタ
数値リテラル関係
8進数リテラル
数値リテラルの先頭に0o
を追加することで8進数で数値を表現できます。次の例をご覧ください。
数値区切り文字
桁数が大きな数値リテラルを見やすく表現するため、リテラルの先頭と終端、小数点の前後以外の任意の場所にアンダースコア(_
)を使用できます。次の例をご覧ください。
例外処理関係
例外の再スロー
try...catch
で受取ったExceptionオブジェクトを使って例外を再スローできるようになりました。次の例をご覧ください。
この変更について詳しく知るには、Losetta#40を参照してください。
catch
のオブジェクトの省略
例外がスローされた際例外の内容を報告するオブジェクトが不要な場合、受取る引数が不要になりました。次の例をご覧ください。
例外フィルター
自分で処理できる例外のみを選択して処理できるようになりました。
例外フィルターでは、when(condition)
を使って条件に一致する例外のみを補足できます。
次の例をご覧ください。
try
{
throw 0x00;
}
catch(e) when (e.ErrorCode == 0x00)
{
print("0x00例外がスローされました");
}
catch
{
print("それ以外の例外がスローされました");
}
配列アクセス関係
スプレッド構文
スプレッド構文(...ary
)を使用することで配列式中で、別の配列にある要素を展開できるようになりました。
次の例をご覧ください。
この変更について詳しく知るには、Losetta#100を参照してください。
逆から指定できる配列添え字
次のように、配列中の後ろからn番目を取得するには配列添え字に^
を使用できます。
var ary = [1,2,3,4];
print(ary[^2]); //後ろから2番目を表示する (出力 : 3)
// ary[^2]は以下のコードに動的書き換えされる
ary[ary.Length-2];
その他フロー関係
case
およびdefault
でフォールスルーを予防するように
従来、switch
中のcase
およびdefault
は一致した部分より下がすべて実行されていました。しかし多くの場合一致したcase
のみ処理させたいため、break
を使用していました。
Alice3.0では規定では、確実にブロックを抜けないcaseやdefaultをエラーにするようになりました。次の例をご覧ください。
var item = 1;
switch(item)
{
case 0:
{
break; //breakで抜けている
}
case 1:
{
print("itemは1でした"); //breakで抜けていないためエラー
}
case 2:
{
return; //returnで抜けている
}
}
#fall_through enable
をコードの先頭に記述してください。
#fall_through enable
var item = 1;
switch(item)
{
case 0:
{
print("itemは0でした");
}
case 1:
{
print("itemは1でした"); // 0のときも実行される
}
}
複数case
に一致する処理を書けるように
次のように、複数のcase
のいずれか(OR)に該当する処理を簡単に書けるようになりました。
var item = 1;
switch(item)
{
case 0:
case 1:
case 2:
{
print("itemは0,1,2のいずれかでした");
break;
}
default:
{
print("itemはそれ以外の値でした");
break;
}
}
特定範囲内で変数を読み取り専用にするreadonly
文の導入
次のように、readonly
文を使うことで特定範囲内で変数を読み取り専用にできるようになりました。これによって変数が書き換えられない範囲を明記できます。
スコープを明示的に表記できるblock
文の実装
次のように、block
文および空の文を使用して変数のスコープを明示的に表現できます。
block
{
var item = 1;
}
print(item); //これはエラーになる
{
var item = 1;
}
print(item); //これもエラーになる
ステートメント形式のラムダ式
ステートメント形式のラムダ式を使用すると、処理が複数行にわたるような複雑なデリゲートをラムダ式で表現できます。また、丸かっこ()
を用いた引数リストにも対応したため、引数を持たないラムダ式を作成できます。次の例をご覧ください。
// ステートメント形式のラムダ式
var hello = (name) =>
{
print(name);
}
hello("John");
// 引数を持たないラムダ式
var hello2 = () => print("Hello");
この変更について詳しく知るには、Losetta#16を参照してください。
前置インクリメント・デクリメント演算子の導入
前置インクリメント演算子(++i
)は、変数の値を1増やし、増やした後の値を返します。
反対に、前置デクリメント演算子(--i
)を使用すると変数の値が1減ります。
次の例をご覧ください。
パフォーマンスの改善
ソースコードの解析をロケール非依存のものに変更することで、すべてのコードで解析にかかる時間が2桁~3桁倍高速化しました。 また、空の文やブロックの解析をスキップする最適化を行ったほか、関数の呼び出しも高速化しました。次のソースコードの実行にかかる時間をAlice2.3に準拠したLosettaとAlice3.0に準拠したLosettaで比較しました。
function ForEmpty()
{
for(var i = 0;i < 10000;i++)
{
;
}
}
function ForIncrement()
{
for(var i = 0;i < 10000;i++)
{
str++;
}
}
function ForStringAppend()
{
var str = "";
for(var i = 0;i < 10000;i++)
{
str += "\"\"\"\"\"\"\"\"\"";
}
}
Function | Mean | Error | StdDev | Max | Min |
---|---|---|---|---|---|
ForEmpty(2.0) | 560.87ms | 0.17ms | 5.30ms | 592ms | 555ms |
ForIncrement(2.0) | 820.36ms | 6.17ms | 61.70ms | 1051ms | 765ms |
ForStringAppend(2.0) | 1437.83ms | 3.13ms | 31.34ms | 1527ms | 1393ms |
ForEmpty(3.0) | 5.34ms | 0.05ms | 1.60ms | 25ms | 5ms |
ForIncrement(3.0) | 86.76ms | 1.71ms | 17.13ms | 254ms | 82ms |
ForStringAppend(3.0) | 277.82ms | 1.63ms | 16.34ms | 428ms | 269ms |
この測定結果は一例です。すべての環境で同様の結果が得られるとは限りません。
使用できる文字の変更
Alice3.0では、制御文字やフォーマット文字、非文字など一部の文字を無視するようになりました。これは、Unicode標準にのっとり、他の多くの言語と同様にプログラムの可読性を高め、誤解を避けるための措置です。 使用できない文字の厳密な定義はProcessing.csおよびConstants.csを参照してください。
CLIの改善
- デバッグ出力のインデントに使用される文字をを空白文字4つに変更
- デバッグ出力の表示色を白色から水色に変更
- REPLで呼び出し履歴(スタックトレース)が表示されるように
- ARM版macOS(M1Mac)をネイティブサポート
- 可能な場合スタックトレースの文字列表現に名前空間を表示するように
- パス設定用のスクリプトを生成するように
alice
で例外発生時に式を挿入できるように- 構文エラー時に表示されるエラーを改善
実装の変更
- C#のchar型との相互変換を実装
- 別の場所にあるアセンブリを読み込めるように
APIの変更
env_lang_name
関数とenv_lang_version
関数を追加file_read_codepage
関数を実装file_write_text
とfile_append_text
でコードページ番号を指定して書き込めるように- より安全にファイルを読み書きできる
file_write_encrypt
とfile_read_decrypt
を実装 exit
関数を丸括弧なしで呼び出せるように変更bytes.ToString(number codePage)
で文字コードを指定して文字列化できるようにbytes.ToBase64
を実装- 文字列や配列の拡張メソッド追加
- スクリプトから直接ParsingScriptを制御できるように
- DNS関連の関数実装
- 対数関数(math_log)、逆数の近似値を求める関数(
Math_ReciprocalEstimate
)、階乗を求める関数(math_factorial)を実装 - 表明関連の関数を実装
string.PadCenter
関数を実装ProcessInfo
オブジェクトで終了コードをとれるように- 数値丸め処理APIのオプションを追加