31
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Swiftでは関数はファーストクラスである。一体どういうことです?

Last updated at Posted at 2014-06-21

関数は型を持つ

関数が型を持つ?
関数型ってやつ?
関数型言語ってやつ?
そうなの?

よくわかりませんが、型を持つらしいです。

後で書きますが、Swiftでは関数がファーストクラスであり、
それはつまり、関数の引数や戻り値に 関数そのもの を指定できるということです。

Swiftは静的型付け言語です。
関数の引数や戻り値に関数を指定できるのであれば、 静的型付け言語では当然それは型を持つ必要がある。
関数が型を持てばこそ、静的型付け言語でも関数がファーストクラスとして振る舞える。

まあこれはなんか論理的に納得できるような気がします。

関数の型って何すか?

関数の型は、その引数と戻り値によって決まります。

例えば以下の関数があったとき、その型は (Int, Int) -> Int と示すことができます。

func addTwoInts(a: Int, b: Int) -> Int {
    return a + b
}

以下はまた別の関数ですが、その型は上のやつと同じで (Int, Int) -> Int ですね。

func multiplyTwoInts(a: Int, b: Int) -> Int {
    return a * b
}

上記した2つの関数 addTwoIntsmultiplyTwoInts(Int, Int) -> Int 型を持つなら、
これはもしや (Int, Int) -> Int 型の変数(とか定数とか)に代入できるんじゃないでしょうか?

// addTwoIntsを代入してみる
var mathFunction: (Int, Int) -> Int = addTwoInts
println(mathFunction(2, 3)) //=> 結果は『5』

// multiplyTwoIntsを代入してみる
mathFunction = multiplyTwoInts
println(mathFunction(2, 3)) //=> 結果は『6』

できました!

ここでちゃんと理解しておかないといけないのは、代入している addTwoIntsmultiplyTwoInts ってやつらは、
関数の実行結果ではなく関数そのもの であることです。

だから、mathFunction(2, 3) のような形で実行できます。

じゃあ、引数とか戻り値の無い関数の型はどうなるんすか?

引数や戻り値がない関数、例えばこんなやつです。

func printHelloWorld() {
    println("hello, world")
}

この関数の型は、そのまま「引数なし、戻り値なし」を示す () -> () が型になります。

let printFunction: () -> () = printHelloWorld
printFunction()

簡単ですね。

実際には、上記したような関数は引数や戻り値を持っていないのではなく、
Void型の引数を持っている、という方が正確である。
したがって、型を以下のようにして書くこともできた。

let printFunction: Void -> Void = printHelloWorld
printFunction()

これで、関数の型、ってやつが大体分かりました。

引数に関数をとる関数

何度もいいますが、Swift では関数がファーストクラスの型です。
ファーストクラスの型 というのは「The Swift Programming Guide」では以下のように説明されています。

  • 関数は別の関数を戻り値として返すことができる
  • 関数はその引数に別の関数をとることができる

いわゆる高階関数というやつですね。
「関数の型」をちゃんと理解していれば、特に難しいことは何もなく、そのまま、普通の関数の記法のルールにしたがうだけです。

まずは、引数に関数をとる関数です。

さっきの addTwoInts 関数の定義をまた使います。

func printMathResult(mathFunction: (Int, Int) -> Int, a: Int, b: Int) {
    println("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5) // => "Result: 8” と表示する

printMathResult の引数は3つありますね。

  • mathFunction: (Int, Int) -> Int
  • a: Int
  • b: Int

1つ目の引数が、(Int, Int) -> Int 型の関数であることがわかると思います。
なので、printMathResult 関数内でその引数の関数を実行できています。
全然難しくないですね。

戻り値で関数を返す関数

これも、「関数の型」が分かっていればそのまま書くだけです。

まず、以下のような2つの関数があります。

func stepForward(input: Int) -> Int {
    return input + 1
}

func stepBackward(input: Int) -> Int {
    return input - 1
}

どちらも関数の型は、(Int) -> Int ですね。

じゃあ、それらの関数を返す別の関数を定義して使ってみましょう。

func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
    return backwards ? stepBackward : stepForward
}

var currentValue = 3
let moveNearerToZero = chooseStepFunction(currentValue > 0)

chooseStepFunction 関数の戻り値は (Int) -> Int 型の関数になってます。
なので、その型をもった stepForwardstepBackward 関数を戻り値に指定できます。

ここでも、戻り値は 関数の実行結果ではなく関数そのもの であることをちゃんと理解しておいてください。

ネストされた関数

関数を戻り値で返す場合、その関数内でネストされた関数を定義して返すことができます。

func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
    func stepForward(input: Int) -> Int { return input + 1 }
    func stepBackward(input: Int) -> Int { return input - 1 }
    return backwards ? stepBackward : stepForward
}

が、これはクロージャの説明に回します。
説明の機会があるかは不明です!

ぶっちゃけこのへん、Swift の新しい考え方というわけではなく、Objective-C でも SEL型とかブロックとかあったわけで、
そのあたり使いこなしてた人にとっては今更な話だったりすると思います。
とはいえSwiftでかなり使いやすくなっているので、やっと普通に自分でも使えるような気がしませんか。

31
30
7

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
31
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?