2016-01-27 36 views
6

Có cách nào đơn giản và xác định trong Swift để kiểm tra xem có gì đó là khối/chức năng có thể gọi không? Trong một số ngôn ngữ, đó là một điều tầm thường, nhưng có lẽ tôi đang xem xét điều này từ một quan điểm sai lầm trong Swift? Hãy xem xét những điều sau đây.Kiểm tra xem biến có phải là một khối/chức năng/có thể gọi trong 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 

Swift biết rằng tất cả đều có chức năng, mặc dù không có loại dữ liệu như vậy. Tôi có thể kiểm tra bằng cách sử dụng một chữ ký vững chắc, nhưng có thể có một tình huống mà tôi không quan tâm đến chữ ký * và chỉ đơn giản là muốn gọi nó. Ví dụ:

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

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

tôi có thể viết lại nó như thế này, mà sẽ làm việc cho VoidBool kiểu trả về, nhưng làm điều này cho tất cả các loại là điên rồ, đặc biệt là kể từ khi tôi không quan tâm về điều đó, nhưng trình biên dịch hiện ...

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 

* Đồng ý, không quan tâm đến chữ ký là tội lỗi. Đối với các đối số vì lợi ích, chúng ta chỉ cần không quan tâm về kiểu trả về.

+0

Tôi đoán một vấn đề cần xem xét sẽ là, bạn sẽ không chỉ phải biết, nếu biến là callable, nhưng nếu họ hy vọng các thông số. Biết nếu một cái gì đó có thể gọi được là không có giá trị nếu bạn không biết đó là thông số. –

+0

Đúng, do đó chú thích cuối trang. –

+0

Nhưng tôi không nói về kiểu trả về. Tôi đang nói về các thông số. –

Trả lời

4

Bạn có thể kiểm tra biểu diễn Chuỗi của .dynamicType của sự kiện có thể gọi cho chuỗi con ->. Không siêu thanh lịch, nhưng nó hoạt động:

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 

Tất nhiên, như Marcus ROSSEL chỉ ra trong các bình luận ở trên, bạn vẫn sẽ không biết gì về các thông số của callable (nhưng có lẽ đó có thể là bước tiếp theo để tìm hiểu, cho rằng bạn biết đó là một cuộc gọi).


Bổ sung cho câu hỏi OP dưới đây: chỉ là thảo luận kỹ thuật và không được đề xuất kỹ thuật.

Bạn sử dụng phương pháp tương tự như trên để kiểm tra xem đối số chức năng có bị đóng không có đối số hay không (() -> (...)) hoặc không có đối số hoặc kiểu trả về (() ->()), v.v. Sử dụng cách tiếp cận này, bạn có thể định nghĩa một hàm chung gọi đối số được gửi đến hàm chỉ khi nó là một kiểu đóng cửa nhất định. Đối với số điện thoại "trong chức năng-gọi" này, bạn sẽ phải sử dụng loại chuyển đổi thành loại đóng cửa dự kiến, nhiều như bạn đã mô tả trong câu hỏi trên của mình. Nó có thể sẽ khó khăn để phá vỡ phương pháp "không chung chung" này.w.t. gọi đóng cửa. Một vài ví dụ sau đây.

/* 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) 
     } 
    } 
} 

Ví dụ gọi:

/* 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

Cảm ơn, tôi đã nghĩ về điều gì đó như vậy, nó chỉ cảm thấy một chút hacky, như bạn đã đề cập. –

+0

Giữa, giả sử, giả sử chúng tôi phát hiện ra rằng chúng tôi có một đóng cửa mà không có tham số.Có cách nào để gọi nó mà không biết kiểu trả về không? Hay chúng ta vẫn phải đúc nó như trong ví dụ cuối cùng trong câu hỏi? –

+0

@IanBytchek Tôi không chắc tôi hoàn toàn hiểu những gì bạn muốn làm, nhưng: trước hết chúng tôi không _have_ để sử dụng kiểu trả về, vì vậy cho 'let b:() -> (Bool) = { return true} 'chúng ta chỉ có thể gọi nó và bỏ qua kiểu trả về, tức là' b() '. Tuy nhiên, nếu bạn đang đề cập đến việc gọi hàm 'foo' trong hàm ở trên (nếu _we_ biết nó là một đóng), thì sẽ phức tạp hơn: trình biên dịch không biết gì về kiểu' foo' (ngay cả khi chúng ta làm). Ngoài ra, vì các bao đóng là các kiểu không mang tính danh nghĩa, chúng ta không thể sử dụng cách tiếp cận có thể thay thế, trong đó chúng ta gõ ràng buộc một số hàm để chỉ đóng các tham số zero-parameter. – dfri

Các vấn đề liên quan