如何将print函数作为参数传递给forEach?

How to pass the print function as argument to a forEach?

提问人:R u c k s a c k 提问时间:5/9/2023 最后编辑:R u c k s a c k 更新时间:5/10/2023 访问量:66

问:

该函数可以打印整数,可以像 一样调用。我想在数组上做同样的事情。printprint(5)[Int]

但是,尝试在 上执行相同的操作会失败。forEach

[1,2,3].forEach(print)

expression failed to parse:
error: repl.swift:20:17: error: cannot convert value of type
 '(Any..., String, String) -> ()' to expected argument type 
'(Int) throws -> Void'

[1,2,3].forEach(print)
                ^

如何让 Swift 在不求助于下面列出的解决方法的情况下执行从 到 的转换?(Any, ... String, String) -> ()(Int) throws -> Void

函数类型在参数位置上不是逆变的吗,这意味着 因为是 的超类型,是 的子类型(因为与 相同),因此根据里氏替换原理,在这种情况下应该是一个完全可以接受的参数吗?AnyInt(Any, ... String, String) -> ()(Int) throws -> Void()VoidprintforEach

是 (variadic AND optional!) 参数的数量有问题吗?(Any, ... String, String)

解决方法

这些工作,但对我来说似乎没有必要。 使用速记参数名称创建内联闭包或定义函数都可以。[1,2,3].forEach { print($0) }func printInt(_ n: Int) { print(n) }; [1,2,3].forEach(printInt)

这表明问题至少部分出在论点的数量上。但是,我不清楚这里发生了什么,并希望得到任何帮助。

swift 类型 闭包 逆变 liskov-substitution-principle

评论

1赞 Sweeper 5/9/2023
“是数量(可变参数和可选)的问题吗?参数 (Any, ...字符串,字符串)?是的
0赞 R u c k s a c k 5/9/2023
谢谢@Sweeper - 除了我在问题的“解决方法”部分所做的“包装”之外,我还能做些什么更简洁的方法来解决这个问题吗?
1赞 Sweeper 5/9/2023
你可以声明你自己的“函数转换器”,将 a 变成 ,但我想说这也是“包装”。你只需要把它包起来。(Any..., String, String) -> Void(Any) -> Void

答:

1赞 Geoff Hackworth 5/9/2023 #1

如果您只关心使用,这是另一种解决方法:print

extension Sequence {
    func printEach() {
        for element in self {
            print(element)
        }
    }
}

[1,2,3].printEach()

或者适用于任何类型的解决方法的变体:printInt

func printElement(_ element: Any) {
    print(element)
}

[1,2,3].forEach(printElement)

两者都不是很吸引我,只是使用闭包对阅读代码的人来说可能更清楚。print($0)

1赞 Sweeper 5/9/2023 #2

是 (variadic AND optional!) 参数的数量有问题吗?(Any, ... String, String)

是的。函数类型参数没有语法,即“这是一个可选参数”。当您被视为第一类对象时,其可选参数将自动变为必需参数。print

let p = print
// p's type is (Any..., String, String) -> Void

func notOptionalPrint(_ items: Any..., separator: String, terminator: String) { ... }
let q = notOptionalPrint
// q's type is also (Any..., String, String) -> Void

此外,第一个参数的类型是 。这与 Any 不同。它不是 or 的超类型,因此 LSP 在这里不适用,即使可选参数问题神奇地解决了。Any...IntAny

因此,如果要将其直接传递给 ,则必须以某种方式包装它。与其只包装它,不如考虑包装它:forEachIntAny

func print(_ x: Any) {
    // need "Swift." here, otherwise infinite recursion
    Swift.print(x)
}

然后,由于 LSP,您可以将其直接传递给任何元素类型的集合。forEach

不过就我个人而言,我不会打扰,只是使用 .forEach { print($0) }