まずはじめに、Bool型の引数を二つ受け取って、その論理和を返す関数logical_sum()を作ります。(sumple1)
sumple1
func logical_sum(_ lhs:Bool,_ rhs:Bool)->Bool{ if lhs{ print("結果は true です") return true }else{ print("結果は \(rhs) です") return rhs } } logical_sum(true,true) //結果は true です logical_sum(true,false) //結果は true です logical_sum(false,true) //結果は true です logical_sum(false,false) //結果は false です
sumple1ではlogical_sum関数は二つのBool値を受け取る関数です。このlogical_sum関数の二つの引数にそれぞれ別の関数out_lhs,out_rhs関数の戻り値を渡すようにしてみます。(sumple2)
sumple2
func logical_sum(_ lhs:Bool,_ rhs:Bool)->Bool{ if lhs{ print("true") return true }else{ print(rhs) return rhs } } func out_lhs()->Bool{ print("out_lhs()関数実行中") return true } func out_rhs()->Bool{ print("out_rhs()関数実行中") return false } //この場合lhs(),rhs()両方実行されてからor()が実行される。 logical_sum(out_lhs(),out_rhs()) //out_lhs()関数実行中 //out_rhs()関数実行中 //true
logical_sum関数の第一引数にout_lhs関数の戻り値を、
logical_sum関数の第二引数にout_rhs関数の戻り値を渡しています。
実行結果を見ればわかりますが、この場合まずout_lhs関数、out_rhs関数の両方が実行された後にor関数が実行されます。sumple2では第一引数がtrueであっても必ずout_rhs関数が実行されます。
しかしこのlogical_sum関数は第一引数がtrueの場合、第二引数の結果が何であろうと関係なく、logical_sum関数の戻り値はtrueになります。つまり第一引数がtrueの場合out_rhs関数を実行する必要がない、ということです。
ということで、sumple2を改良してout_rhs関数を実行する必要がある時だけ実行するようにしてみます。(sumple3)
sumple3
func logical_sum(_ lhs:Bool,_ rhs:()->Bool)->Bool{ if lhs{ print("true") return true }else{ let in_rhs=rhs() //結果的に定数in_rhsにはout_rhs関数の戻り値がセットされる。 print(in_rhs) return in_rhs } } func out_lhs()->Bool{ print("out_lhs()関数実行中。") return true } func out_rhs()->Bool{ print("out_rhs()関数実行中。") return false } //この場合最初に第一引数のout_lhs()は実行されるが、右側の引数はクロージャーを渡している //だけなので引数評価時点ではout_rhs()は実行されない。 //or関数内でrhs()とした時に、渡したクロージャーが実行→ //クロージャーの中のout_rhs()が実行される、という流れ。 logical_sum(out_lhs(),{return out_rhs()}) //out_lhs()関数実行中。 //true
logical_sum関数の第二引数rhsには
{ return out_rhs() }
というクロージャーが渡されます。簡略的に記述されていますが、
{ ()->Bool in
return out_rhs()
}
と同じことですね。(クロージャーの簡略的な記法についてはパート1をご覧ください。)
logical_sum関数の第二引数rhsにクロージャーが渡される。
↓
logical_sum関数内のelse節でrhs(つまり引数として渡されたクロージャー)が実行される。
↓
クロージャー内でout_rhs関数が実行される。
ということで、out_rhs関数が実行されるのは、logical_sum関数の第一引数lhsがfalseの時のみ、lhsがtrueの場合else節は実行されないのでout_rhs関数が実行されることはない、ということになります。
sumple3をオートクロージャー属性を用いて記述してみます。(sumple4)
sumple4
func logical_sum(_ lhs:Bool,_ rhs:@autoclosure ()->Bool)->Bool{ if lhs{ print("結果は true です。") return true }else{ let in_rhs = rhs() print("結果は \(in_rhs)です。") return in_rhs } } func out_lhs()->Bool{ print("out_lhs関数実行中。") return true } func out_rhs()->Bool{ print("out_rhs関数実行中。") return false } logical_sum(out_lhs(),out_rhs()) //out_lhs関数実行中。 //結果は true です。
sumple4ではlogical_sum関数の第二引数に
out_rhs()
を渡しています(22行目)。これはクロージャーではありませんが、1行目でlogical_sum関数の第二引数にオートクロージャー属性を指定していますので、22行目のlogical_sum関数の第二引数は自動的に
{ out_rhs() }
となり、クロージャーになります。
関数(logical_sum)の引数として渡された式(out_rhs())を自動的に中かっこ({})で囲んでクロージャーにする、これがオートクロージャー属性です。
オートクロージャー属性は引数( rhs )の型名( ()->Bool )の前に
@autoclosure
と記述して指定します。
まとめ
sumple2のように関数logical_sumの引数として「関数out_rhs実行の式」を渡すと、out_rhsの実行タイミングをコントロールすることはできません。
しかしsumple3,sumple4のように
関数logical_sumの引数としてクロージャーを渡す
↓
実行させたいタイミングでクロージャー(を受け取った引数rhs)を実行させる
上記のようにすることでout_rhs実行のタイミングをコントロールすることができました。