1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

UWSCRAdvent Calendar 2023

Day 6

ユーザー定義関数

Last updated at Posted at 2023-12-05

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

どこにでも書ける

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

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

print f() // 実行されない
uwscr
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)
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?