2016-01-27 7 views
6

में ब्लॉक/फ़ंक्शन/कॉल करने योग्य है या नहीं, यह जांचने के लिए स्विफ्ट में एक सरल और निश्चित तरीका है कि कुछ कॉल करने योग्य ब्लॉक/फ़ंक्शन है या नहीं? कुछ भाषाओं में यह एक छोटी सी चीज है, लेकिन शायद मैं इसे स्विफ्ट में गलत परिप्रेक्ष्य से देख रहा हूं? निम्नलिखित को धयान मे रखते हुए।जांचें कि चर एक स्विफ्ट

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 '() ->()' 

मैं इसे इस, जो Void और Bool वापसी प्रकार के लिए काम करेंगे की तरह फिर से लिखने सकते हैं, लेकिन हर प्रकार के लिए यह कर रही है, पागल है, खासकर जब से मैं इसके बारे में परवाह नहीं है, लेकिन संकलक है ...

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 
बेशक

, के रूप में मार्कस Rossel टिप्पणी में बताते हैं इसके बाद के संस्करण, आप अभी भी प्रतिदेय के मापदंडों के बारे में कुछ भी पता नहीं होता (लेकिन शायद कि अगले कदम हो सकता है यह पता लगाने के लिए कि आपको पता है कि यह एक कॉल करने योग्य है)। सिर्फ एक तकनीकी चर्चा, और नहीं की सिफारिश की तकनीक: नीचे ऑप्स सवालों के संबंध में


अलावा।

यदि समारोह तर्क तर्क (() -> (...)) या न तर्क के साथ एक और न ही लौट प्रकार (() ->()) के बिना एक बंद है, और इतने पर जाँच करने के लिए ऊपर दिए गए उसी दृष्टिकोण का उपयोग करें। इस दृष्टिकोण का उपयोग करके, आप एक सामान्य फ़ंक्शन को परिभाषित कर सकते हैं जो फ़ंक्शन पर भेजे गए तर्क को केवल तभी कॉल करता है जब यह एक निश्चित बंद प्रकार का हो। इस "इन-फ़ंक्शन-कॉल" के लिए, आपको अपेक्षित बंद करने के प्रकार के प्रकार का रूपांतरण करना होगा, जैसा कि आपने ऊपर दिए गए अपने क्यू में वर्णित किया है। शायद यह "गैर-सामान्य" दृष्टिकोण w.r.t. को बाधित करना मुश्किल होगा। कॉलिंग बंद करना। कुछ उदाहरण नीचे दिए गए हैं।

/* 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 मुझे यकीन नहीं है कि मैं पूरी तरह से समझता हूं कि आप क्या करना चाहते हैं, लेकिन: सबसे पहले हम वापसी प्रकार का उपयोग करने के लिए _have_ नहीं करते हैं, इसलिए 'चलिए बी:() -> (बूल) = { सच वापस लौटें} 'हम इसे सिर्फ कॉल कर सकते हैं और वापसी प्रकार को अनदेखा कर सकते हैं, यानी' बी() '। हालांकि, अगर आप ऊपर दिए गए फ़ंक्शन के भीतर 'foo' को कॉल करने का जिक्र कर रहे हैं (यदि _we_ जानते हैं कि यह बंद है), तो ट्रिकियर होगा: कंपाइलर' foo' के प्रकार के बारे में नहीं जानता है (भले ही हम करते हैं)। इसके अलावा, चूंकि बंद गैर-नाममात्र प्रकार हैं, इसलिए हम संभावित रूप से वैकल्पिक दृष्टिकोण का उपयोग नहीं कर सकते हैं, जहां हम कुछ फ़ंक्शन को केवल शून्य-पैरामीटर बंद करने के लिए बाध्य करते हैं। – dfri

संबंधित मुद्दे