ユーザー定義関数

ユーザー定義関数もいくつかの変更点があり、利便性が向上しています。また、無名関数を定義できるようになり関数の戻り値として関数を返す、ということもできるようになっています。

# どこにでも書ける

UWSCでは関数定義はスクリプトの下部に書かなければいけない (≒関数定義等の下に書いたコードは実行されない) という制限がありましたが、UWSCRならどこに書いても大丈夫です。

```php:uwsc
function f()
    result = "hello, uwsc!"
fend

print f() // 実行されない
```

```php:uwscr
function f()
    result = "hello, uwscr!"
fend

print f() // hello, uwscr!
```
# 引数定義

特殊な引数について

## 参照渡し

UWSCでは`var`キーワードで引数を参照渡しにできましたが、UWSCRではそれに加え`ref`というキーワードも使えます。

```php
procedure p(ref s)
    s += " uwscr!"
fend

s = "hello"
p(s)
print s // hello uwscr!
```

## 配列表記

UWSCでは`引数名[]`で配列変数を受けられる引数を定義できましたが、UWSCRではこの表記は互換性のためだけに残されていて、動作への影響は特にありません。

```php
// 配列引数表記
procedure p(arr[])
fend
// 以下と同じです
procedure p(arr)
fend
```

## 可変長引数

引数名の前に`args`または`prms`キーワードをつけることで、可変長の引数を受けられるようになります。また、その引数は関数内では配列になります。可変長引数は必ず最後の引数である必要があります。

```php
function f(args a)
    result = a
fend

print f(1)       // 1
print f(1, 2, 3) // [1, 2, 3]
```

```php
// 以下は定義できません
function f(args a, b)  // 後ろに引数があるのはダメ
function f(ref args a) // 参照渡しとの併用もダメ
function f(args a = 1) // デフォルト引数との併用もダメ
```

# 引数の型チェック

`引数名: 型名`と記述することで引数に受けられる値の型を制限できます。異なる型の引数が渡された場合はエラーになります。参照渡しやデフォルト引数との併用も可能です。

```php
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)の値                 |

:::note info
クラス名と列挙体名は定義済みのclassまたはenumの名前になります。

```php
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に含まれない値)
```
:::

# 無名関数

無名関数はその名の通り名前を持たない関数で、関数の実体を返す式を変数で受けて使います。通常のユーザー定義関数のように特殊な引数の記述も使えます。

```php
f = function(n: number)
    result = n * 2
fend

print f(3) // 6
```

無名関数は定義された時点でのスコープ内の変数等を取り込みます。

```php
a = "hello"
f = function()
    result = "<#a> world!"
fend
print f() // hello world!
```

この性質を利用してクロージャを返すことも出来ます。

```php
function enclosure()
    a = "hello"
    result = function(s: string)
        result = "<#a> <#s>!"
    fend
fend

closure = enclosure()
print closure("world") // hello world!
```

## 関数式

無名関数の簡易表記です。`|引数名 => 式|`と記述します。引数は通常の関数定義と同じように書けます。処理を記述する部分は**式しか書けない**という制限があります。また、通常の関数と異なり式が返す値がそのまま戻り値となります (resultに代入する必要はありません)。

```php
f = | a: number, b: number => a + b |
print f(3, 5) // 8

// 処理部に文は書けない
f = | a => print a| // 構文エラー
```

処理部分の式は`;`区切りで複数記述できます。その場合は最後の式が返す値が戻り値となります。

```php
f = | a: number, b: number => a *= 2; b *= 3; a + b |
print f(3, 5) // 21

// ※代入および複合代入は文のように振る舞いますが実態は式なので、関数式で利用可能です
```

関数式は即時関数としても利用可能です。関数式に`()`を付けることで即実行されます。引数も渡せます。

```php
print |s => "hello <#s>!"|("world") // hello world!
// これは以下と同じです
f = |s => "hello <#s>!"|
print f("world")
```

# オブジェクトとしての関数

ユーザー定義関数や組み込み関数はそれ自体がオブジェクトでもあります。そのため変数に代入したり、関数の引数として渡すということもできます。

```php:組み込み関数を代入
// 組み込み関数msgboxを変数に代入
mb = msgbox
// msgbox関数を実行できる
mb("hello!")
```

```php:関数を引数として渡す
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)
```