2019/12/15 Swift クロージャー パート4(オートクロージャー属性)

まずはじめに、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実行のタイミングをコントロールすることができました。

 

 

コメントを残す

メールアドレスが公開されることはありません。