Swiftのmapは処理だけしてVoid返すことは出来ないから気ィ付けや
追記
この記事の情報は古いので注意して下さい。
最新のXcodeでは、mapに返り値を持たないクロージャを渡しても問題ありません。
追記ここまで
SwiftのArrayは、非破壊的なメソッド、map
を持っています。素晴らしい。
enumerateObjectsUsingBlock:
とか書いてられませんよホントに。メソッド名が長いのはともかくBlocksの記法がfuckingblocksyntaxでうんざりします。
map
の宣言はfunc map<U>(transform: (T) -> U) -> Array<U>
となっています。
Haskellでいうところのmap :: (t -> u) -> [t] -> [u]
ですね。
T型の要素を持つArrayと、T型を引数に取ってU型を返すクロージャがあります。T型のArrayの全ての要素にクロージャを適用し、U型のArrayを返すのがmap
です。
まずはInt
のArrayをmap
してInt
のArrayを返すコード
let numbers :Array<Int> = [1,2,3,4] // Int型のArray let doubled :Array<Int> = numbers.map { (n :Int) -> Int in return n * 2 // 全ての要素を2倍する } println(doubled) //=> [2, 4, 6, 8]
次に、Int
のArrayをmap
してString
のArrayを返すコード
let numbers :Array<Int> = [1,2,3,4] // Int型のArray let repeated :Array<String> = numbers.map { (n :Int) -> String in var retVal :String = "" for _ in 0..n { retVal += "★" } // ★をn回繰り返す return retVal } println(repeated) //=> [★, ★★, ★★★, ★★★★]
最後に、Int
のArrayをmap
するが、処理だけして値は返さない--Voidを返す--コード
let numbers :Array<Int> = [1,2,3,4] // Int型のArray numbers.map { (n :Int) -> Void in // ここでランタイムエラー println(n) }
死にます。ちなみにビルドは通り、実行時に死にます。
Rubyなんかでは、こういうmapは普通に書けます。
numbers = [1,2,3,4] numbers.map { |n| print n } #=> 1234
考察
まず、前提として「Void型を要素にもつArray」は宣言できます。つまり、コンパイルが通ります。
var voids :Void[] var voids2 :()[]
これに、実際にVoidのArrayを代入すると、コンパイルは通りますが、RuntimeErrorで死にます。
var voids :Void[] voids = [()] // ここで死ぬ
空のArrayですら代入できません。コンパイルは通りますが、同様にRuntimeErrorで死にます。
var voids :Void[] = [] // ここで死ぬ
Swiftのmap
は非破壊的なので、map自体は(再)代入を行いません。
そのため、このコードでは代入は行われませんが、やはり死んでしまいます。
let numbers :Array<Int> = [1,2,3,4] // Int型のArray numbers.map { (n :Int) -> Void in // ここでランタイムエラー println(n) }
あくまで推測ですが、map
を走らせる際に、内部的にはArray<Void>
のインスタンスを作って処理結果を随時代入しようとしていて、その際に死ぬのでしょう。
「Swiftのmapは処理だけしてVoid返すことは出来ない」と同時に、そもそもArray<Void>
は使ってはいけない、と考えてよいと思います。
なお、配列に対して副作用だけ起こしてなにも値を返したくない際は、大人しくfor in
を使えばよいです。
let numbers :Array<Int> = [1,2,3,4] // Int型のArray for n :Int in numbers { println(n) } // 出力 1 2 3 4
追記
Twitter等で「Voidを返す」って表現がアレ、との指摘があります。 たしかにアレなんですが、ドキュメントに「値を返さないってのはな、実は()を返してるんだ」とあるので見逃して下さい。
Strictly speaking, the sayGoodbye function does still return a value, even though no return value is defined. Functions without a defined return type return a special value of type Void. This is simply an empty tuple, in effect a tuple with zero elements, which can be written as ().