Swift @autoclos का उपयोग कैसे करें


148

मैंने देखा assertकि स्विफ्ट में लिखते समय कि पहला मान टाइप किया गया है

@autoclosure() -> Bool

के Tमाध्यम से अस्तित्व का परीक्षण करने के लिए एक सामान्य मूल्य वापस करने के लिए एक अतिभारित विधि के साथ LogicValue protocol

हालांकि हाथ पर सवाल करने के लिए सख्ती से चिपका। यह एक चाहते प्रतीत होता है @autoclosureकि एक रिटर्न Bool

एक वास्तविक क्लोजर लिखना जो कोई पैरामीटर नहीं लेता है और एक बूल लौटाता है, काम नहीं करता है, यह चाहता है कि मैं इसे संकलित करने के लिए क्लोजर को कॉल करूं, जैसे:

assert({() -> Bool in return false}(), "No user has been set", file: __FILE__, line: __LINE__)

हालाँकि बस एक बूल काम करता है:

assert(false, "No user has been set", file: __FILE__, line: __LINE__)

तो क्या चल रहा है? क्या है @autoclosure?

संपादित करें: @auto_closure नाम बदल दिया गया था@autoclosure

जवाबों:


269

एक फ़ंक्शन पर विचार करें जो एक तर्क लेता है, एक सरल समापन जो कोई तर्क नहीं लेता है:

func f(pred: () -> Bool) {
    if pred() {
        print("It's true")
    }
}

इस फ़ंक्शन को कॉल करने के लिए, हमें एक क्लोजर में पास करना होगा

f(pred: {2 > 1})
// "It's true"

यदि हम ब्रेसिज़ को छोड़ देते हैं, तो हम एक अभिव्यक्ति में गुजर रहे हैं और यह एक त्रुटि है:

f(pred: 2 > 1)
// error: '>' produces 'Bool', not the expected contextual result type '() -> Bool'

@autoclosureअभिव्यक्ति के चारों ओर एक स्वचालित बंद बनाता है। इसलिए जब कॉलर एक अभिव्यक्ति की तरह लिखता है 2 > 1, तो यह स्वचालित रूप से एक क्लोजर में लपेट दिया जाता है ताकि {2 > 1}इसे पारित होने से पहले बन जाए f। इसलिए अगर हम इसे फंक्शन पर लागू करते हैं f:

func f(pred: @autoclosure () -> Bool) {
    if pred() {
        print("It's true")
    }
}

f(pred: 2 > 1)
// It's true

तो यह एक अभिव्यक्ति के साथ एक क्लोजर में लपेटने की आवश्यकता के बिना काम करता है।


2
वास्तव में पिछले एक, काम नहीं करता है। यह होना चाहिएf({2 >1}())
रुई पेर्स

@JoelFischer मैं @JackyBoy जैसी ही चीज़ देख रहा हूं। कॉलिंग का f(2 > 1)काम करता है। कॉलिंग के f({2 > 1})साथ विफल रहता है error: function produces expected type 'Bool'; did you mean to call it with '()'?। मैंने इसे एक खेल के मैदान में और स्विफ्ट आरईपीएल के साथ परीक्षण किया।
ओले बेगमैन

मैं किसी भी तरह आखिरी जवाब के रूप में दूसरे से आखिरी उत्तर को पढ़ता हूं, मुझे दोहरी जांच करनी होगी, लेकिन यह समझ में आता है कि क्या यह विफल हो गया, जैसा कि आप मूल रूप से एक क्लोजर के अंदर एक क्लोजर डाल रहे हैं, जो मुझे समझ में आता है।
जोएल फिशर

3
इस कारण के बारे में एक ब्लॉग पोस्ट है कि उन्होंने डेवलपर
.apple.com/swift/

5
महान व्याख्या। यह भी ध्यान दें कि स्विफ्ट 1.2 'autoclosure' में अब पैरामीटर घोषणा की एक विशेषता है, तो यह हैfunc f(@autoclosure pred: () -> Bool)
मासा

30

यहाँ एक व्यावहारिक उदाहरण है - मेरा printओवरराइड (यह स्विफ्ट 3 है):

func print(_ item: @autoclosure () -> Any, separator: String = " ", terminator: String = "\n") {
    #if DEBUG
    Swift.print(item(), separator:separator, terminator: terminator)
    #endif
}

जब आप कहते हैं print(myExpensiveFunction()), मेरा printओवरराइड स्विफ्ट को ओवरशेड करता है printऔर उसे बुलाया जाता है। myExpensiveFunction()इस प्रकार एक बंद में लिपटे और मूल्यांकन नहीं किया जाता है । यदि हम रिलीज़ मोड में हैं, तो इसका मूल्यांकन कभी नहीं किया जाएगा, क्योंकि item()इसे कॉल नहीं किया जाएगा। इस प्रकार हमारे पास एक संस्करण है printजो रिलीज़ मोड में इसके तर्कों का मूल्यांकन नहीं करता है।


मुझे पार्टी के लिए देर हो रही है, लेकिन मूल्यांकन का क्या प्रभाव है myExpensiveFunction()? यदि आप स्वत: प्रकटीकरण का उपयोग करने के बजाय फ़ंक्शन को प्रिंट करने के लिए पास करते हैं print(myExpensiveFunction), तो इसका क्या प्रभाव होगा? धन्यवाद।
crom87

11

डॉक्स से ऑटो_क्लोजर का विवरण:

आप auto_closure विशेषता को एक फ़ंक्शन प्रकार पर लागू कर सकते हैं जिसमें एक पैरामीटर प्रकार () है और जो एक अभिव्यक्ति का प्रकार लौटाता है (देखें प्रकार विशेषताएँ)। एक स्वत: प्रकटन फ़ंक्शन अभिव्यक्ति के बजाय निर्दिष्ट अभिव्यक्ति पर एक अंतर्निहित बंदी को पकड़ता है। निम्न उदाहरण एक बहुत ही सरल मुखर फ़ंक्शन को परिभाषित करने में auto_closure विशेषता का उपयोग करता है:

और यहाँ उदाहरण सेब का उपयोग इसके साथ है।

func simpleAssert(condition: @auto_closure () -> Bool, message: String) {
    if !condition() {
        println(message)
    }
}
let testNumber = 5
simpleAssert(testNumber % 2 == 0, "testNumber isn't an even number.")

मूल रूप से इसका क्या मतलब है कि आप एक बूलियन अभिव्यक्ति को पास करते हैं क्योंकि यह एक बंद होने के बजाय पहला तर्क है और यह स्वचालित रूप से आपके लिए इसे बंद कर देता है। इसलिए आप झूठी विधि से गुजर सकते हैं क्योंकि यह एक बूलियन अभिव्यक्ति है, लेकिन एक बंद पास नहीं कर सकता।


15
ध्यान दें कि आपको वास्तव में @auto_closureयहां उपयोग करने की आवश्यकता नहीं है। कोड इसके बिना ठीक काम करता है func simpleAssert(condition: Bool, message: String) { if !condition { println(message) } }:। उपयोग करें @auto_closureजब आपको किसी तर्क का बार-बार मूल्यांकन करने की आवश्यकता होती है (जैसे, यदि आप एक समान whileकार्य को लागू कर रहे थे ) या आपको एक तर्क के मूल्यांकन में देरी करने की आवश्यकता है (उदाहरण के लिए, यदि आप शॉर्ट-सर्कुलेटिंग को लागू कर रहे थे &&)।
नथन

1
@ नथन हाय, नथन। तुम मुझे के उपयोग के संबंध एक नमूना का हवाला देते हैं कृपया सकते हैं autoclosureएक साथ whileकी तरह समारोह? मुझे यह पता नहीं लगता है। अग्रिम में ही बहुत शुक्रिया।
उन्नीलीग

@connor आप स्विफ्ट 3 के लिए अपने जवाब को अपडेट करना चाह सकते हैं
jarora

4

यह https://airspeedvelocity.net/2014/06/28/extending-the-swift-language-is-cool-but-be-careful/ का एक उपयोगी मामला दिखाता है@autoclosure

अब, सशर्त अभिव्यक्ति पहले पैरामीटर के रूप में तब तक पारित हो जाती है जब तक कि स्वचालित रूप से एक क्लोजर अभिव्यक्ति में लपेटा नहीं जाता है और लूप के चारों ओर हर बार कहा जा सकता है

func until<L: LogicValue>(pred: @auto_closure ()->L, block: ()->()) {
    while !pred() {
        block()
    }
}

// doSomething until condition becomes true
until(condition) {
    doSomething()
}

2

यह एक करीबी कॉल में घुंघराले ब्रेसिज़ से छुटकारा पाने का एक तरीका है, सरल उदाहरण:

    let nonAutoClosure = { (arg1: () -> Bool) -> Void in }
    let non = nonAutoClosure( { 2 > 1} )

    let autoClosure = { (arg1: @autoclosure () -> Bool) -> Void in }
    var auto = autoClosure( 2 > 1 ) // notice curly braces omitted

0

@autoclosureएक फ़ंक्शन पैरामीटर है जो एक पका हुआ फ़ंक्शन (या लौटा हुआ प्रकार) closureस्वीकार करता है इस बीच एक सामान्य एक कच्चे फ़ंक्शन को स्वीकार करता है

  • @ अनाउंसमेंट तर्क प्रकार पैरामीटर '() होना चाहिए
    @autoclosure ()
  • @ डिपॉज़िट किसी भी फ़ंक्शन को केवल उपयुक्त रिटर्न प्रकार के साथ स्वीकार करते हैं
  • बंद का परिणाम मांग द्वारा गणना की जाती है

आइए एक उदाहरण देखें

func testClosures() {

    //closures
    XCTAssertEqual("fooWithClosure0 foo0", fooWithClosure0(p: foo0))
    XCTAssertEqual("fooWithClosure1 foo1 1", fooWithClosure1(p: foo1))
    XCTAssertEqual("fooWithClosure2 foo2 3", fooWithClosure2(p: foo2))

    XCTAssertEqual("fooWithClosure2 foo2 3", fooWithClosure2(p: { (i1, i2) -> String in
        return "fooWithClosure2 " + "foo2 " + String(i1 + i2)
    }))

    //@autoclosure
    XCTAssertEqual("fooWithAutoClosure HelloWorld", fooWithAutoClosure(a: "HelloWorld"))

    XCTAssertEqual("fooWithAutoClosure foo0", fooWithAutoClosure(a: foo0()))
    XCTAssertEqual("fooWithAutoClosure foo1 1", fooWithAutoClosure(a: foo1(i1: 1)))
    XCTAssertEqual("fooWithAutoClosure foo2 3", fooWithAutoClosure(a: foo2(i1: 1, i2: 2)))

}

//functions block
func foo0() -> String {
    return "foo0"
}

func foo1(i1: Int) -> String {
    return "foo1 " + String(i1)
}

func foo2(i1: Int, i2: Int) -> String {
    return "foo2 " + String(i1 + i2)
}

//closures block
func fooWithClosure0(p: () -> String) -> String {
    return "fooWithClosure0 " + p()
}

func fooWithClosure1(p: (Int) -> String) -> String {
    return "fooWithClosure1 " + p(1)
}

func fooWithClosure2(p: (Int, Int) -> String) -> String {
    return "fooWithClosure2 " + p(1, 2)
}

//@autoclosure
func fooWithAutoClosure(a: @autoclosure () -> String) -> String {
    return "fooWithAutoClosure " + a()
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.