我对Swift文档的理解是参数标签,而不仅仅是参数数据类型,是编译时函数签名固有的。因此,编译器理解这两个版本的f
具有不同的类型:它们都接受一个String
参数,但标签不同。
func f(foo str : String) -> String { "foo" }
func f(bar str : String) -> String { "bar" }
f(foo: "hi") // "foo"
f(bar: "hi") // "bar"
然而,我对下面的行为感到困惑。g
和h
也有不同的类型,因为它们有不同的参数标签。但是,即使缺少参数标签,这段代码也可以编译和运行:
func f(foo str : String) -> String { "foo" }
func f(bar str : String) -> String { "bar" }
func g(foo str : String) -> String { f(foo: str) }
func h(bar str : String) -> String { f(bar: str) }
(0 < 100 ? g : h)("hello") // "foo" (why?)
但是,如果我们引入第二个具有不同参数标签的h
,代码将无法编译,因为现在我们有两种冲突类型的h
:
func f(foo str : String) -> String { "foo" }
func f(bar str : String) -> String { "bar" }
func g(foo str : String) -> String { f(foo: str) }
func h(bar str : String) -> String { f(bar: str) }
func h(foo str : String) -> String { f(foo: str) }
(0 < 100 ? g : h)("hello") // ambiguity error
我的问题是:为什么在第二个例子中,Swift编译器对类型检查采取了更宽松的观点?我理解没有类型歧义。但是这种行为似乎仍然违反了Swift关于表达式返回类型需要匹配的非常强的策略。
如果我们认为参数标签是类型签名的一部分,那么像0 < 100 ? g : h
这样的表达式甚至不应该编译,因为g
和h
有不同的参数标签,因此不同的类型。
事实:Swift中的任何函数都可以简化为闭包,当它们被传递时,它们实际上就是闭包。f
、g
和h
都可以简化为(String) -> String
类型的闭包。
接下来,你的(some_computation)("hello")
代码实际上做的是告诉编译器你想创建一个匿名函数(又名闭包),你用"hello"论点。
因此,(0 < 100 ? g : h)("hello")
指示Swift编译器将g
和h
函数转换为(String) -> String
类型的闭包。闭包没有参数标签,因此任何带标签的函数都可以匹配。
然而,在有问题的代码片段中,编译器不能再唯一地识别哪个h
";重载"了。使用。因此,它退出了。
如果您想要明确匹配h
函数之一,那么您需要完全限定它:
(0 < 100 ? g : h(foo:))("hello")
规则对成员函数也是一样的,只要编译器可以决定传递哪个闭包,它就会匹配函数名,而不必指定它的完整名称。
要点是在Swift中你不是循环函数,而是循环闭包。没有办法指定变量、函数参数或属性来拥有带标签的参数。