お前は何を言っているんだ
var filteredLandmarks: [Landmark] {
landmarks.filter { landmark in
(!showFavoritesOnly || landmark.isFavorite)
}
}
apple公式のSwiftUI Tutrialにこのようなコードがありました。
はい、初心者にはよくわかりません。具体的には
[Landmark] {/*...*/}
landmarks.filter {/*...*/}
この2つの書き方です。まず、
配列の直後にクロージャってなんだよ!
.filterも、直後にクロージャをもってきて何やってるんだよ!
そもそもクロージャってなんだよ!
私は上記の書き方ではfilteredLandmarksは型を明示しただけで、初期値は入っていないと一瞬思ったのですが、きちんと入っているようですね。そもそも自分の中で変数・定数を宣言する以下の3つの方法のうち、初期値を入れられるのはイコールで左右を結ぶものだけと考えていました。
var a = 1 //型推論
var a:Int = 1 //型明示
var a:Int //型のみ、初期値なし?
しかし最初の例では宣言にイコールは使われていないものの、型([Landmark])を与え、
その後のクロージャの中で[Landmark]型であるlandmarksから配列のfilterメソッドを使って条件に合う要素だけを取り出し、その新たな配列を返している、ということなのでしょうか?
うーん、クロージャ君、君のことがよくわからんのだよ。
.filter {landmark in
/*...*/}
の中身はFor in文などと似ているので理解できているかと思います。
配列の要素を一つ一つ評価する際、その要素がlandmarkに渡されているんですよね?
(これをイテレーターというのかな?)
そして論理和で、
お気に入りのみ表示するフィルター(showFavoritesOnly:Bool)がfalse
またはその要素がお気に入りされているか(landmark.isFavorite)がtrue
ならtrue。そしてfilterはtrueを返す要素を返す。
そうですよね、ね!
クロージャとは?
そもそもクロージャとは何でしょうか?
Google先生によれば、無名関数です。
あ、なるほどね、無名関数ね。
は?
プログラミングあるある、知らない単語を調べると、別の知らない単語で説明されている。
ただ、いい感じに説明してくれているサイトもありました。
どうやらクロージャ(無名関数)は文字通り「名前のついていない関数」のようですね。
例えば
//クロージャ
let a = {print("テストです")}
a() ///出力:テストです。
//関数
func t(){
print("テストです。")
}
t() //出力:テストです。
この2つのコードは同じ、どちらも関数なんですね。
基本的な書き方としては
{(引数:引数の型)->返り値の型 in
処理
return 返り値}
例えば
let closure = {(fruit:String,price:Int) -> String in
let str = "\(fruit)は\(price))円です。"
return str}
let casher = closure("グレープ",100)
print(casher) //出力 グレープは100円です。
となります。省略形は複数あり
{()->返り値の型 in ...return 返り値} //引数なし 返り値あり
{()->返り値の型 in ...返り値} //処理が一行の場合はreturnを省略できる
{()->Void in ...} //引数なし 返り値なし
{()->() in ...} //引数なし 返り値なし
{...} //引数なし 返り値なし ここまで省略できる。
と表現できます。
クロージャの基礎がわかった現在、もう一度最初のコードのわからなかったところを見直してみます。
[Landmark] {/*...*/}
landmarks.filter {/*...*/}
うーん、やっぱりわからん!
普通配列やメソッドの直後に関数ぼーんって置いたりしないですよね?
どうしても理解ができず、ググってみたところ、こいつはどうも Trailing closure という記法らしいです。
Closure を最後の引数として取るメソッドの場合、Closure を () の外側に書くことができる>(Trailing closure )
https://blog.personal-factory.com/2016/01/07/how-to-swift-closure/
なるほど、じゃあ本来の書き方なら.filter({})ってことか!
ここで.filterの定義を見てみます。
@inlinable public func filter(_ isIncluded: (Element) throws -> Bool) rethrows -> [Element]
確かにfilterの引数は「Element(要素)を貰いBoolを返すクロージャ」となっていますね!
(throwsはエラー処理らしいが今は知らん!!!)
[Landmark]{}は?
ちょっとここで力尽きました。大粒の涙を流して寝ます。
追記:
うーん、playgroundで配列を以下のように宣言したら、普通にできたんですよね。
つまりこれは宣言の仕方の一種類ってことなのかな?
var abc:[Int]{[1,2,3]}
abc //[1,2,3]
ただし
let abc:[Int]{[1,2,3]}
abc //[1,2,3]
はできませんでした。つまりまず abc:[Int] ここで型を明示し初期値を入れずに宣言し、その後クロージャで値が渡されている?
ただ、もちろんですが以下はできるんですよね。なんなんだ[]{} この構造!
let abc:[Int ]
abc = [1,2,3]
追記2:
var landmarkIndex: Int {
modelData.landmarks.firstIndex(where: { $0.id == landmark.id })!
}
このような書き方を見つけました。つまりこれは変数の宣言の仕方の一種なんですね。こういうものだと覚えとこう。
var abc:Int{ここがIntを返すクロージャ}
これで初期値を定めた変数abcが宣言できます。
参考: