ユーザー定義関数もいくつかの変更点があり、利便性が向上しています。また、無名関数を定義できるようになり関数の戻り値として関数を返す、ということもできるようになっています。
どこにでも書ける
UWSCでは関数定義はスクリプトの下部に書かなければいけない (≒関数定義等の下に書いたコードは実行されない) という制限がありましたが、UWSCRならどこに書いても大丈夫です。
function f()
result = "hello, uwsc!"
fend
print f() // 実行されない
function f()
result = "hello, uwscr!"
fend
print f() // hello, uwscr!
引数定義
特殊な引数について
参照渡し
UWSCではvar
キーワードで引数を参照渡しにできましたが、UWSCRではそれに加えref
というキーワードも使えます。
procedure p(ref s)
s += " uwscr!"
fend
s = "hello"
p(s)
print s // hello uwscr!
配列表記
UWSCでは引数名[]
で配列変数を受けられる引数を定義できましたが、UWSCRではこの表記は互換性のためだけに残されていて、動作への影響は特にありません。
// 配列引数表記
procedure p(arr[])
fend
// 以下と同じです
procedure p(arr)
fend
可変長引数
引数名の前にargs
またはprms
キーワードをつけることで、可変長の引数を受けられるようになります。また、その引数は関数内では配列になります。可変長引数は必ず最後の引数である必要があります。
function f(args a)
result = a
fend
print f(1) // 1
print f(1, 2, 3) // [1, 2, 3]
// 以下は定義できません
function f(args a, b) // 後ろに引数があるのはダメ
function f(ref args a) // 参照渡しとの併用もダメ
function f(args a = 1) // デフォルト引数との併用もダメ
引数の型チェック
引数名: 型名
と記述することで引数に受けられる値の型を制限できます。異なる型の引数が渡された場合はエラーになります。参照渡しやデフォルト引数との併用も可能です。
function f(n: number)
result = n * 2
fend
// 参照渡し
procedure p(ref s: string)
s += " world!"
fend
// デフォルト引数
function f2(n: number = 5)
result = n + 10
fend
print f(5) // 10
print f("hoge") // エラー
型名 | 受けられる型 |
---|---|
string | 文字列 |
numver | 数値 |
bool | 真偽値 (TRUE/FALSE) |
array | 配列 |
hash | 連想配列 |
func | ユーザー定義関数 |
uobject | UObject |
クラス名※ | クラスオブジェクトのインスタンス |
列挙体名※ | 列挙体(enum)の値 |
クラス名と列挙体名は定義済みのclassまたはenumの名前になります。
procedure p1(o: MyClass)
fend
procedure p2(e: MyEnum)
fend
class MyClass
procedure MyClass
fend
endclass
class MyClass2
procedure MyClass2
fend
endclass
o = MyClass()
p1(o) // ok
// 異なるクラスはダメ
// o2 = MyClass2()
// p1(o2) // エラー
enum MyEnum
foo
bar
baz
endenum
p2(MyEnum.foo) // ok
p2(1) // ok (MyEnum.barと同値なので)
p2(3) // エラー (MyEnumに含まれない値)
無名関数
無名関数はその名の通り名前を持たない関数で、関数の実体を返す式を変数で受けて使います。通常のユーザー定義関数のように特殊な引数の記述も使えます。
f = function(n: number)
result = n * 2
fend
print f(3) // 6
無名関数は定義された時点でのスコープ内の変数等を取り込みます。
a = "hello"
f = function()
result = "<#a> world!"
fend
print f() // hello world!
この性質を利用してクロージャを返すことも出来ます。
function enclosure()
a = "hello"
result = function(s: string)
result = "<#a> <#s>!"
fend
fend
closure = enclosure()
print closure("world") // hello world!
関数式
無名関数の簡易表記です。|引数名 => 式|
と記述します。引数は通常の関数定義と同じように書けます。処理を記述する部分は式しか書けないという制限があります。また、通常の関数と異なり式が返す値がそのまま戻り値となります (resultに代入する必要はありません)。
f = | a: number, b: number => a + b |
print f(3, 5) // 8
// 処理部に文は書けない
f = | a => print a| // 構文エラー
処理部分の式は;
区切りで複数記述できます。その場合は最後の式が返す値が戻り値となります。
f = | a: number, b: number => a *= 2; b *= 3; a + b |
print f(3, 5) // 21
// ※代入および複合代入は文のように振る舞いますが実態は式なので、関数式で利用可能です
関数式は即時関数としても利用可能です。関数式に()
を付けることで即実行されます。引数も渡せます。
print |s => "hello <#s>!"|("world") // hello world!
// これは以下と同じです
f = |s => "hello <#s>!"|
print f("world")
オブジェクトとしての関数
ユーザー定義関数や組み込み関数はそれ自体がオブジェクトでもあります。そのため変数に代入したり、関数の引数として渡すということもできます。
// 組み込み関数msgboxを変数に代入
mb = msgbox
// msgbox関数を実行できる
mb("hello!")
function math(f: func)
a = 3
b = 5
// 渡された関数を呼ぶ
result = f(a, b)
fend
function add(n, m)
result = n - m
fend
print math(add) // 8
print math(|n, m => n * m|) // 15
// 組み込み関数も渡せる
function dialog(f)
result = f("hello world!")
fend
print dialog(msgbox)
print dialog(input)