2016-01-27 13 views
6

Swiftで呼び出し可能なブロック/関数があるかどうかを確認する簡単で明確な方法はありますか?いくつかの言語ではそれはささいなことですが、おそらく私はこれをSwiftの間違った視点から見ていますか?以下を考慮する。変数がSwiftでブロック/関数/呼び出し可能であることを確認してください

func foo(){ print("foo") } 
var bar:() ->() = { print("bar") } 
var baz:() -> (Bool) = { print("baz"); return true } 

print(foo) // (Function) 
print(bar) // (Function) 
print(baz) // (Function) 

print(foo is() ->()) // true 
print(bar is() ->()) // true 
print(baz is() ->()) // false 
print(baz is() -> (Bool)) // true 

スウィフトは、そのようなデータタイプはありませんが、すべて機能していることがわかります。私は確固たる署名を使って確認できますが、署名*が気にせず、単にそれを呼び出すような状況があるかもしれません。たとえば:

func call(callable:() ->()) { 
    callable() 
} 

call(foo) // foo 
call(bar) // bar 
call(baz) // error: cannot convert value of type '() -> (Bool)' to expected argument type '() ->()' 

私はVoidBool戻り値の型のために働くであろう、このようにそれを書き換えるが、すべてのタイプのためにこれをやってはクレイジーです、私はそれを気にしない、特に以来、コンパイラはありませんすることができます... *

func call(callable: Any) { 
    if let block:() ->() = callable as?() ->() { 
     block() 
    } else if let block:() -> (Bool) = callable as?() -> (Bool) { 
     block() 
    } 
} 

call(foo) // foo 
call(bar) // bar 
call(baz) // truely baz 

はない署名を気にすることは罪である、同意します。議論のために、戻り値の型を気にしないでください。

+0

私は変数が呼び出し可能だった場合は、知っているしなければならないだけで、それはパラメータを期待している場合ことを、考慮すべき問題は次のようになりますね。呼び出し可能なものがあるかどうかを知ることは、そのパラメータがわからなければ価値がありません。 –

+0

真、したがって脚注。 –

+0

しかし、私は戻り値の型について話しているわけではありません。私はパラメータについて話しています。 –

答えて

4

呼び出し可能な文字列の.dynamicTypeの文字列表現は、部分文字列->の存在を確認できます。超エレガントではないが、それは動作します:もちろん

func isAClosure<T>(foo: T) -> Bool { 
    return String(foo.dynamicType).containsString("->") 
} 

var a :() ->() = { print("Foobar") } 
var b : (Double) -> (Bool) = { $0 > 0 } 
var c : Int = 1 

isAClosure(a) // true 
isAClosure(b) // true 
isAClosure(c) // false 

、マーカス・ロッセルは、上記のコメントで指摘するように、あなたはまだ呼び出し可能なのパラメータについては何も知らないだろう(おそらくそれは、次のステップかもしれませんあなたがそれが呼び出し可能であることを知っていることを考えればわかる)。単に技術的な議論ではなく、推奨技術:以下のOPSの質問に関して


追加。

あなたは関数の引数は、ように引数なしで閉鎖(() -> (...))または引数や戻り値の型(() ->())もないとの一つであり、そしてかどうかを確認するために、上記と同じアプローチを使用しています。このアプローチを使用すると、関数が特定のクロージャータイプである場合にのみ関数に送信される引数を呼び出すジェネリック関数を定義できます。このの "関数呼び出し中"の場合は、上のQで説明したのと同じように、予想されるクロージャタイプに型変換する必要があります。おそらく、この「非ジェネリック」アプローチを回避することは難しいでしょう。 クロージャを呼び出します。以下にいくつかの例を示します。

/* Example functions */ 
func isAVoidParamClosure<T>(foo: T) -> Bool { 
    let bar = String(foo.dynamicType).componentsSeparatedByString(" -> ") 
    return bar.count > 1 && (bar.first?.characters.count ?? 0) == 2 
} 

func callIfVoidVoidClosure<T>(foo: T) { 
    let bar = String(foo.dynamicType).componentsSeparatedByString(" -> ") 
    if bar.count > 1 && !(bar.map{ $0 == "()" }.contains(false)) { 
     if let foo = foo as?() ->() { 
      foo() 
     } 
    } 
} 

func isASingleDoubleReturnTypeClosure<T>(foo: T) -> Bool { 
    let bar = String(foo.dynamicType).componentsSeparatedByString(" -> ") 
    return bar.count > 1 && bar[1] == "Double" 
     /* rhs of '&&' lazily evaluated: [1] ok */ 
} 

func printTwoTimesResultOfVoidDoubleClosure<T>(foo: T) { 
    if isAVoidParamClosure(foo) && isASingleDoubleReturnTypeClosure(foo) { 
     if let foo = foo as?() -> Double { 
      let a: Double = 2*foo() 
      print(a) 
     } 
    } 
} 

例コール:

/* Example calls */ 
let a :() ->() = { print("Foobar") } 
let b : (Double) -> (Bool) = { $0 > 0 } 
let c :() -> Double = { 21.0 } 
let d : Int = 1 

isAVoidParamClosure(a) // true 
isAVoidParamClosure(b) // false 
isAVoidParamClosure(c) // true 
isAVoidParamClosure(d) // false 

callIfVoidVoidClosure(a) // Prints "Foobar" 
callIfVoidVoidClosure(b) 
callIfVoidVoidClosure(c) 
callIfVoidVoidClosure(d) 

printTwoTimesResultOfVoidDoubleClosure(a) 
printTwoTimesResultOfVoidDoubleClosure(b) // Prints "42.0" 
printTwoTimesResultOfVoidDoubleClosure(c) 
printTwoTimesResultOfVoidDoubleClosure(d) 
+0

ありがとう、私はそのようなことについて考えました、あなたが言及したように、ちょっとハッキリと感じます。 –

+0

たとえば、パラメータを取らないクロージャがあることがわかったとします。戻り値の型を知らなくてもそれを呼び出す方法はありますか?あるいは、質問の最後の例のように、まだキャストする必要がありますか? –

+0

@IanBytchekあなたがしたいことを十分理解しているかどうかはわかりませんが、最初は戻り値の型を使う必要はありません。let let :() - >(Bool)= { return true} 'これを呼び出すだけで、戻り値の型、つまり' b() 'を無視することができます。 (_we_が、それはクロージャだ知っていれば)あなたは上記の関数内で呼び出す 'foo'を参照している場合は、トリッキー次のようになります。コンパイラは、(我々が行う場合でも)' foo'の種類についてのnothinを知っています。また、閉包は非公称型であるため、ゼロパラメータ閉包だけを取るようないくつかの関数を制約に入力する代替アプローチを使用することはできません。 – dfri

関連する問題