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?

Functions

Last updated at Posted at 2024-10-27

関数は、特定のタスクを実行する一連のステートメントです。関数にはパラメータ(入力)とオプショナルの戻り値(出力)があります。関数には型があり、関数の型はパラメータの型と戻り値の型で構成されます。

関数は値であり、すなわち定数や変数に代入でき、他の関数の引数として渡すことができます。この動作はしばしば「ファーストクラス(first-class)関数」と呼ばれます。

Function Declarations

関数は、funキーワードを使用して宣言し、宣言名、パラメータ、オプションの戻り値の型、関数が呼び出された際に実行されるコードを続けます。

パラメータは括弧で囲む必要があります。戻り値がある場合は、コロン(:)でパラメータの後に続けます。関数のコードは、開始と終了の波括弧で囲む必要があります。

各パラメータには名前を指定する必要があり、これは関数内で引数の値として使用される名前です。

追加の引数ラベルを指定すると、関数呼び出し時にラベルを使用してパラメータの引数値を指定する必要があります。

引数ラベルを使用すると、コードがより明確になり、読みやすくなります。例えば、同じ型の複数の引数がある場合、引数の順序に関する混乱を避けることができます。

引数ラベルは、関数呼び出しの観点から意味をなすように名前を付ける必要があります。

引数ラベルはパラメータ名の前に置きます。特別な引数ラベル_は、関数呼び出し時に引数ラベルを省略できることを示します。関数宣言で引数ラベルが宣言されていない場合、パラメータ名が関数宣言の引数ラベルとなり、関数呼び出しではパラメータ名を引数ラベルとして使用する必要があります。

各パラメータにはtype annotationが必要であり、これはコロン(:)の後にパラメータ名を続けて指定します。

関数呼び出しでは、パラメータ型の下位型(サブタイプ)を引数を指定することができます。

パラメータのデフォルト値や可変個引数関数(任意の数の引数を取る関数)のサポートはありません

// Declare a function named `double`, which multiples a number by two.
//
// The special argument label _ is specified for the parameter,
// so no argument label has to be provided in a function call.
//
fun double(_ x: Int): Int {
    return x * 2
}

// Call the function named `double` with the value 4 for the first parameter.
//
// The argument label can be omitted in the function call as the declaration
// specifies the special argument label _ for the parameter.
//
double(2)  // is `4`

一部のパラメータには引数ラベルを必須とし、他のパラメータには引数ラベルを必須としないことも可能です。

// Declare a function named `clamp`. The function takes an integer value,
// the lower limit, and the upper limit. It returns an integer between
// the lower and upper limit.
//
// For the first parameter the special argument label _ is used,
// so no argument label has to be given for it in a function call.
//
// For the second and third parameter no argument label is given,
// so the parameter names are the argument labels, i.e., the parameter names
// have to be given as argument labels in a function call.
//
fun clamp(_ value: Int, min: Int, max: Int): Int {
    if value > max {
        return max
    }

    if value < min {
        return min
    }

    return value
}

// Declare a constant which has the result of a call to the function
// named `clamp` as its initial value.
//
// For the first argument no label is given, as it is not required by
// the function declaration (the special argument label `_` is specified).
//
// For the second and this argument the labels must be provided,
// as the function declaration does not specify the special argument label `_`
// for these two parameters.
//
// As the function declaration also does not specify argument labels
// for these parameters, the parameter names must be used as argument labels.
//
let clamped = clamp(123, min: 0, max: 100)
// `clamped` is `100`
// Declare a function named `send`, which transfers an amount
// from one account to another.
//
// The implementation is omitted for brevity.
//
// The first two parameters of the function have the same type, so there is
// a potential that a function call accidentally provides arguments in
// the wrong order.
//
// While the parameter names `senderAddress` and `receiverAddress`
// are descriptive inside the function, they might be too verbose
// to require them as argument labels in function calls.
//
// For this reason the shorter argument labels `from` and `to` are specified,
// which still convey the meaning of the two parameters without being overly
// verbose.
//
// The name of the third parameter, `amount`, is both meaningful inside
// the function and also in a function call, so no argument label is given,
// and the parameter name is required as the argument label in a function call.
//
fun send(from senderAddress: Address, to receivingAddress: Address, amount: Int) {
    // The function code is omitted for brevity.
    // ...
}

// Declare a constant which refers to the sending account's address.
//
// The initial value is omitted for brevity.
//
let sender: Address = // ...

// Declare a constant which refers to the receiving account's address.
//
// The initial value is omitted for brevity.
//
let receiver: Address = // ...

// Call the function named `send`.
//
// The function declaration requires argument labels for all parameters,
// so they need to be provided in the function call.
//
// This avoids ambiguity. For example, in some languages (like C) it is
// a convention to order the parameters so that the receiver occurs first,
// followed by the sender. In other languages, it is common to have
// the sender be the first parameter, followed by the receiver.
//
// Here, the order is clear – send an amount from an account to another account.
//
send(from: sender, to: receiver, amount: 100)

関数呼び出しの引数の順序は、関数宣言のパラメータの順序と一致していなければなりません。

// Declare a function named `test`, which accepts two parameters, named `first` and `second`
//
fun test(first: Int, second: Int) {
    // ...
}

// Invalid: the arguments are provided in the wrong order,
// even though the argument labels are provided correctly.
//
test(second: 1, first: 2)

関数はネストすることができ、すなわち、関数のコードがさらに別の関数を宣言することが可能です。

// Declare a function which multiplies a number by two, and adds one.
//
fun doubleAndAddOne(_ x: Int): Int {

    // Declare a nested function which multiplies a number by two.
    //
    fun double(_ x: Int) {
        return x * 2
    }

    return double(x) + 1
}

doubleAndAddOne(2)  // is `5`

関数はオーバーロードをサポートしていません。

Function Expressions

関数は式としても使用できます。構文は関数宣言と同じですが、関数式には名前がなく、すなわちanonymousであるという点が異なります。

// Declare a constant named `double`, which has a function as its value.
//
// The function multiplies a number by two when it is called.
//
// This function's type is `fun (Int): Int`.
//
let double =
    fun (_ x: Int): Int {
        return x * 2
    }

Function Calls

関数の呼び出しには、関数のパラメータの数と同じ数の引数値を正確に指定する必要があります。

fun double(_ x: Int): Int {
    return x * 2
}

// Valid: the correct amount of arguments is provided.
//
double(2)  // is `4`

// Invalid: too many arguments are provided.
//
double(2, 3)

// Invalid: too few arguments are provided.
//
double()

Function Types

関数型は、funキーワード、関数のパラメータ型、関数の戻り値型で構成されます。

パラメータ型は括弧で囲み、コロン(:)を続けて、戻り値型で終わります。

オプションとして、viewキーワードをfunキーワードの前に含めることで、その型がview関数の型であることを示すことができます。

// Declare a function named `add`, with the function type `fun(Int, Int): Int`.
//
fun add(a: Int, b: Int): Int {
    return a + b
}
// Declare a constant named `add`, with the function type `fun(Int, Int): Int`
//
let add: fun(Int, Int): Int =
    fun (a: Int, b: Int): Int {
        return a + b
    }

関数に返り値の型が指定されていない場合、暗黙的に返り値の型はVoidとなります。

// Declare a constant named `doNothing`, which is a function
// that takes no parameters and returns nothing.
//
let doNothing: fun(): Void =
    fun () {}

括弧も優先順位を制御します。例えば、関数型
fun(Int): fun(): Intは、Int型の引数を1つ受け取り、引数を受け取らずIntを返す別の関数を返す関数の型です。

型[fun(Int): Int; 2]は、1つの整数を受け取り、1つの整数を返す2つの関数の配列型を指定します。

引数ラベルは関数型の一部ではありません。これにより、異なる引数ラベルを持つ関数(異なる作成者によって書かれた可能性がある)は、パラメータ型と戻り値の型が一致する限り、互換性があるという利点があります。一方、欠点としては関数呼び出しが関数値に対して行われる(原文: function calls to plain function values)場合、引数ラベルを受け入れられないというのがあります。

// Declare a function which takes one argument that has type `Int`.
// The function has type `fun(Int): Void`.
//
fun foo1(x: Int) {}

// Call function `foo1`. This requires an argument label.
foo1(x: 1)

// Declare another function which takes one argument that has type `Int`.
// The function also has type `fun(Int): Void`.
//
fun foo2(y: Int) {}

// Call function `foo2`. This requires an argument label.
foo2(y: 2)

// Declare a variable which has type `fun(Int): Void` and use `foo1`
// as its initial value.
//
var someFoo: fun(Int): Void = foo1

// Call the function assigned to variable `someFoo`.
// This is valid as the function types match.
// This does neither require nor allow argument labels.
//
someFoo(3)

// Assign function `foo2` to variable `someFoo`.
// This is valid as the function types match.
//
someFoo = foo2

// Call the function assigned to variable `someFoo`.
// This does neither require nor allow argument labels.
//
someFoo(4)

Closures

関数は、定義された外部のスコープの変数や定数を参照することができます。それらの変数や定数を囲い込む(クロージャ)ことから、クロージャと呼ばれます。クロージャは、参照する変数や定数から読み取ること、変数に値を代入することができます。

// Declare a function named `makeCounter` which returns a function that
// each time when called, returns the next integer, starting at 1.
//
fun makeCounter(): fun(): Int {
    var count = 0
    return fun (): Int {
        // NOTE: read from and assign to the non-local variable
        // `count`, which is declared in the outer function.
        //
        count = count + 1
        return count
    }
}

let test = makeCounter()
test()  // is `1`
test()  // is `2`

Argument Passing Behavior

関数に引数が渡される際には、それらはコピーされます。したがって、関数に渡された値は、関数がreturnした際に呼び出し元のスコープでは変更されません。この動作は、call-by-value(値渡し)として知られています。

// Declare a function that changes the first two elements
// of an array of integers.
//
fun change(_ numbers: [Int]) {
    // Change the elements of the passed in array.
    // The changes are only local, as the array was copied.
    //
    numbers[0] = 1
    numbers[1] = 2
    // `numbers` is `[1, 2]`
}

let numbers = [0, 1]

change(numbers)
// `numbers` is still `[0, 1]`

パラメータは不変であり、すなわち、それらに値を割り当てることはできません。

fun test(x: Int) {
    // Invalid: cannot assign to a parameter (constant)
    //
    x = 2
}

Function Preconditions and Postconditions

関数には前提条件や事後条件が存在する場合があります。 前提条件や事後条件は、関数の入力(パラメータの値)や出力(戻り値)に制限を設けるために使用することができます。

前提条件は、関数が実行される直前に真である必要があります。 前提条件は関数の一部であり、preキーワードに続いて条件ブロックが続きます。

ポスト条件は、関数の実行直後に真である必要があります。ポスト条件は関数の一部であり、post キーワードで導入され、条件ブロックが続きます。ポスト条件は、もしあれば、プレ条件の後にのみ発生します。

条件ブロックは、1つ以上の条件で構成されます。条件は、ブール値に評価される式です。

条件は改行して記述することも、複数の条件をセミコロンで区切って同じ行に記述することもできます。この構文は、ステートメントの構文に従います。

各条件の後には、コロン(:)の後に説明を任意で記述できます。条件の説明は、条件が失敗した際にエラーメッセージとして使用されます。

ポスト条件では、特別な定数resultは関数の結果を指します。

fun factorial(_ n: Int): Int {
    pre {
        // Require the parameter `n` to be greater than or equal to zero.
        //
        n >= 0:
            "factorial is only defined for integers greater than or equal to zero"
    }
    post {
        // Ensure the result will be greater than or equal to 1.
        //
        result >= 1:
            "the result must be greater than or equal to 1"
    }

    if n < 1 {
       return 1
    }

    return n * factorial(n - 1)
}

factorial(5)  // is `120`

// Run-time error: The given argument does not satisfy
// the precondition `n >= 0` of the function, the program aborts.
//
factorial(-2)

事後条件では、特殊関数beforeを使用して、関数が呼び出される直前の式の値を取得することができます。

var n = 0

fun incrementN() {
    post {
        // Require the new value of `n` to be the old value of `n`, plus one.
        //
        n == before(n) + 1:
            "n must be incremented by 1"
    }

    n = n + 1
}

事前条件と事後条件は、いずれもview コンテキストとみなされます。viewアノテーション付きの関数で許可されていない操作は、条件でも許可されません。特に、条件で関数を呼び出したい場合は、その関数はview関数でなければなりません。

View Functions

関数には、viewとして注釈を付けることで、外部の状態やアカウントの状態を変更しないことを示すことができます。viewの注釈は、関数宣言または式の冒頭に次のように追加できます。

access(all) 
view fun foo(): Void {}

let x = view fun(): Void {}

access(all) 
struct S {

    access(all)
    view fun foo(): Void {}
    
    view init()
}

view注釈を持たないすべての関数は非viewと見なされ、viewコンテキスト内、例えば、view関数内や前提条件や事後条件内では呼び出すことができません。

関数型にもview注釈を付けることができ、開始括弧の後でパラメータリストの前に配置します。例えば、以下の型は有効です。

    let f: view fun (Int): Int = ...
    let h: view fun (): (view fun (): Void) = ...

viewアノテーションのない関数型は、非view型とみなされます。

関数はviewアノテーションに関して共変(covariant; viewをつけたことでつけない状態と相関関係は変わらない)です。つまり、view関数は、同じパラメータと戻り値の型を持つ非view関数のサブタイプです。したがって、以下の宣言は型チェックに合格します。

    let a: view fun (): Void = view fun() {}
    let b: fun (): Void = view fun() {}
    let c: fun (): Void = fun() {}
    let d: fun(view fun(): Void): Void = fun (x: fun(): Void) {} // contravariance

であるがこれらはそうではない:

    let x: view fun (): Void = fun() {}
    let y: fun(fun(): Void): Void = fun(f: view fun(): Void) {} // contravariance

viewコンテキストで許可されていない操作は以下の通りです。

  • viewでない関数の呼び出し(save や load のようなアカウントの状態やストレージを変更する関数を含む)
  • リソースへの書き込みや変更
  • 参照(reference)への書き込みや変更
  • 現在の関数のスコープで定義されていることが静的に判明している変数、またはリソースや参照へのインデックス付き代入や書き込み

例えば、以下のコードは許可されます。

view fun foo(): Int {
    let a: [Int] = []
    a[0] = 3
    return a.length
}

しかし、これは出来ません。

[Int] = []
view fun foo(): Int {
    a[0] = 3
    return a.length
}

これには注意が必要で、場合によっては、viewではない関数であっても、viewのコンテキストで許可される変更のみを行う場合、解析の制限として拒否されることがあります。特に、配列やディクショナリでこの問題が発生することがあり、例えば次のような関数です。

view fun foo(): Int {
    let a: [Int] = [0]
    a[0] = 1
}

これは許容されます。なぜならaは、この関数に限定されているのに対して、

view fun foo(): Int {
    let a: [Int] = [0]
    a.append(1)
}

これは拒否されます。なぜならappend は view ではないからです。

Functions are Values

関数は値(「第一級(first-class)」)であるため、変数やフィールドに割り当てたり、引数として関数に渡したりすることができます。

// Declare a function named `transform` which applies a function to each element
// of an array of integers and returns a new array of the results.
//
access(all)
fun transform(function: fun(Int): Int, integers: [Int]): [Int] {
    var newIntegers: [Int] = []
    for integer in integers {
        newIntegers.append(function(integer))
    }
    return newIntegers
}

access(all)
fun double(_ integer: Int): Int {
    return integer * 2
}

let newIntegers = transform(function: double, integers: [1, 2, 3])
// `newIntegers` is `[2, 4, 6]`

翻訳元->https://cadence-lang.org/docs/language/functions

Flow BlockchainのCadence version1.0ドキュメント (Functions)

Previous << Operators

Next >> Control Flow

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?