関数の定義と呼び出し

おさらい
複数の返り値を持つ関数
仮引数名
初期値を持つ引数
不定個の引数をもつ関数
定数の仮引数と変数の仮引数
関数の型
ローカル関数

おさらい

swiftの関数はだいたいこんな感じ。

//func 関数名(仮引数名: 型) -> 返り値型
func sayHello(personName: String) -> String {
	let greeting = "Hello, " + personName + "!"
	return greeting
}
//String型の引数をとる関数に渡してみる
//Hello, Anna!
println(sayHello("Anna"))
//Hello, Brian!
println(sayHello("Brian"))

//処理とリターンを1行で
func sayHelloAgain(personName: String) -> String {
	return "Hello again, " + personName + "!"
}
//Hello again, Anna!
println(sayHelloAgain("Anna"))

//複数の引数を持つ関数
func halfOpenRangeLength(start: Int, end: Int) -> Int {
	return end - start
}
//9
println(halfOpenRangeLength(1, 10))

//引数のない関数
func sayHelloWorld() -> String {
	return "hello, world"
}
//hello, world
println(sayHelloworld())

ちなみに、返り値はなくてもいいので「自作の関数内でprintlnし何も返さない」というのもアリ。この場合void型(空tuple)になるらしい。

複数の返り値を持つ関数

返り値を複数用意したい場合はtupleの力を借りる。

func count(string: String) -> (vowels: Int, consonants: Int, others: Int) {
	var vowels = 0, consonants = 0, others = 0
	for character in string {
		switch String(character).lowercaseString {
		case "a", "e", "i", "o", "u":
			++vowels
		case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
		"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
			++consonants
		default:
			++others
		}
	}
	return (vowels, consonants, others)
}

複数用意する場合は「func 関数名(引数:型) -> (キー:型, キー:型, ...)」といったように、->の先にtupleを書く。で、返り値を使うには「.キー」というのを後ろにくっつける。

let total = count("some arbitrary string!")
//6 vowels and 13 consonants
println("\(total.vowels) vowels and \(total.consonants) consonants")

仮引数

仮引数とは、関数宣言時にカッコの中にいるあいつらである。

func someFunction(parameterName: Int) {
	//略
}

swiftにはexternal parameter namesというのがある。グローバル仮引数?何だろう。書き方はこんな感じ。

func someFunction(externalParameterName localParameterName: Int) {
	//localParameterNameを使って命令を書く
}

わけわかめなので具体例。わけわかめじゃなくても具体例。
例えば次のような関数があったとする。

func join(s1: String, s2: String, joiner: String) -> String {
	return s1 + joiner + s2
}
join("hello","world",", ")

第1引数と第2引数を第3引数でつなげるというシロモノだが、具体的に関数の定義を見ないと何番目の引数に何を入れて何がどうなる関数なのかわかりづらい。間違ってjoin("hello",", ","world")とか書いちゃうかもしれない。

そこで次のようにしてやるのである。

func join(string s1: String, to String s2: String, withJoiner joiner: String) -> String {
	return s1 + joiner + s2
}

s1さんが「string」というあだ名を、s2さんが「toString」、joinerさんが「withJoiner」をもらったと考えるといいかもしれない。というわけで関数を使う時には次のように書ける。

join(string: "hello", toString: "world", withJoiner: ", ")

「関数名(あだ名: 実引数, ...)」といった感じ。これで何番目の引数が何に使われるかが分かりやすくなった。別にあだ名を必ず明示しなくちゃいけないなんてことはないので、混乱しそうなときだけつかってやればOK。
別名考えるのも面倒だし元々の仮引数の名前(ローカル仮引数名)を流用したいという場合は#をくっつける。

func containsCharacter(#string: String, #characterToFind: Character) -> Bool {
	for character in string {
		if character == characterToFind {
			return true
		}
	}
	return false
}
let containsAVee = containsCharacter(string: "aardvark", characterToFind: "v")

初期値を持つ引数

初期値を設定したい場合はいつも通り「引数: 型 = 初期値」といった感じで。初期値が設定されている引数は引数の中でも後ろに配置しよう。

func join(string s1: String, toString s2: String, withJoiner joiner: String = " ") -> String {
	return s1 + joiner + s2
}

join(string: "hello", toString: "world", withJoiner: "-")	//hello-world
join(string: "hello", toString: "world")	//hello world

ちなみに、初期値を持つ引数は「#」とかをわざわざつけなくても勝手にグローバル仮引数名(?)、external parameter namesを持つので、特に関数定義時に設定していなくても使ってOK。この場合名前はローカルな仮引数の名前と同じになる。

func join(s1: String, s2: String, joiner: String = " ") -> String {
	return s1 + joiner + s2
}

join("hello", "world", joiner: "-")	//hello-world

不定個の引数を持つ関数

題の日本語おかしい。
次のように引数を定義すると、0以上の任意の個数の引数を取ることができるようになる。「仮引数名: 型...」。扱いとしては配列。

func arithmeticMean(numbers: Double...) -> Double {
	var total: Double = 0
	for number in numbers {
		total += number
	}
	return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5)	//3.0
arithmeticMean(3, 8, 19)	//10.0

なお、引数を記す順番は
通常の引数→初期値を持つ引数→不定個の引数
といった順番にする。

定数の仮引数と変数の仮引数

仮引数の中身を操作する場合は、次の例のようにvarを付けて変数にすること。つけないと定数になってしまってエラーになる。swiftは仮引数は基本的に定数のふるまいをするんである……。

//stringの値は途中で書き変わるのでvarを付けてちゃんと変数にする
func alignRight(var string: String, count: Int, pad: Character) -> String{
	let amountToPad = count - countElements(string)
	for _ in 1...amountToPad {
		string = pad + string
	}
	return string
}
let originalString = "hello"
let paddedString = alignRight(originalString, 10, "-")

参照渡し

参照渡しでありがちな例、それは2変数の中身のいれかえ。

func swapTwoInts(inout a: Int, inout b: Int) {
	let temporaryA = a
	a = b
	b = temporaryA
}

上のように、参照渡しされてくる仮引数にinoutとつけてやる。
注意点は、
inoutとつけられた仮引数は初期値を持つことが出来ないのと、
不定個の引数を示す「...」とかいうヤツはinoutつけられないのと、
inoutってつけたらその引数はvarとかletって修飾できないところ。

とにかく、そんな関数を使う時は実引数に&をつけた形で渡す。

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
//someInt is now 107, and anotherInt is now 3
println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")

とまあ、こんな感じで、めでたくswapTwoIntsくんは関数外の変数someIntとanotherIntの値書き換えに成功したわけである。

関数の型

//(Int, Int) -> Int
//2つのInt型引数を取ってInt型を返す関数
func addTwoInts(a: Int, b:Int) -> Int {
	return a + b
}
func multiplyTwoInts(a: Int, b: Int) -> Int {
	return a * b
}

//() -> ()
//引数を取らずvoid(空tuple)を返す関数
func printHelloWorld() {
	println("hello, world")
}

とまあ、swiftでは上記のように関数の型(引数や返り値の型)を表すわけだが、swiftではその関数を変数にしまうことができるので、つまりそういう型の変数が存在するということになる。

var mathFunction: (Int, Int) -> Int = addTwoInts

上のように宣言したmathFunctionは、addTwoInts()に限らずInt型引数2個・返り値Int型1個の関数ならなんでもしまえるので、次のようなことができる。

//Result: 5
println("Result: \(mathFunction(2, 3))")

mathFunction = multiplyTwoInts
//Result: 6
println("Result: \(mathFunction(2, 3))")

別にわざわざvar mathFunction: (Int, Int) -> Intみたいに型を明示しなくてもswiftは空気を読んでくれる。

let anotherMathFunction = addTwoInts	//(Int, Int) -> Int型変数

んでもって、
そういう(Int, Int) -> Int型みたいなものを引数にとる関数も作れる。
次の関数printMathResult()は3つの引数をもち、2つ目と3つ目は普通にInt型なのだが、1つ目が(Int, Int) -> Int型である。

func printMathResult(mathFunction: (Int, Int) -> Int, a: Int, b: Int) {
	println("Result: \(mathFunction(a, b))")
}
//Result: 8
printMathResult(addTwoInts, 3, 5)

んでもって、そんでもって、
そういう関数型を返すような関数も作れる。やりたい放題である。次の例は返り値が(Int) -> Int型の関数である。つまり「引数がInt型1つ、返り値がInt型1つの関数」を返り値とする関数…むにゃむにゃ

//(Int) -> Intな関数
func stepForward(input: Int) -> Int {
	return input + 1
}
func stepBackward(input: Int) -> Int {
	return input - 1
}
//(Bool) -> (Int) -> Intな関数
//引数がBoolで、返り値が(Int) -> Int
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
	return backwards ? stepBackward : stepForward
}

//trueが引数に来たのでmoveNearerToZeroはstepBackward()を得る
var currentValue = 3
let moveNearerToZero = chooseStepFunction(currentValue > 0)

println("Counting to zero:")
while currentValue != 0 {
	//出力してはmoveNearerToZero()っていうかstepBackward()を実行
	println("\(currentValue)... ")
	currentValue = moveNearerToZero(currentValue)
}
println("zero!")
/*
Counting to zero:
3... 
2... 
1...
zero!
*/

ローカル関数

関数の中に作った関数は、その関数中でしか使えない関数となる。
というわけでさっきの(Int) -> Int達をchooseStepFunctionの中に入れてみる。

func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
	//chooseStepFunctionの外からは使えない関数
	func stepForward(input: Int) -> Int { return input + 1 }
	func stepBackward(input: Int) -> Int { return input - 1 }
	
	return backwards ? stepBackward : stepForward
}

//falseが引数に来たのでmoveNearerToZeroはstepForward()を得る
var currentValue = -4
let moveNearerToZero = chooseStepFunction(currentValue > 0)
while currentValue != 0 {
	//出力してstepForward
	println("\(currentValue)... ")
	currentValue = moveNearerToZero(currentValue)
}
println("zero!")
/*
-4... 
-3... 
-2...
-1... 
zero!
*/

↑ PAGE TOP