Функция print может печатать целые числа и может вызываться как print(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, не прибегая к обходным путям, перечисленным ниже?
Не являются ли функциональные типы контравариантными в позиции параметра, что означает, что, поскольку Any является супертипом Int, (Any, ... String, String) -> () является подтипом (Int) throws -> Void (поскольку () совпадает с Void), и, следовательно, по принципу подстановки Лискова, print должен быть вполне приемлемым аргумент forEach в этом случае?
Это проблема с количеством (переменных И необязательных!) параметров в (Any, ... String, String)?
Они работают, но они кажутся мне ненужными.
Создание встроенного замыкания с использованием сокращенного имени аргумента [1,2,3].forEach { print($0) } или определение функции func printInt(_ n: Int) { print(n) }; [1,2,3].forEach(printInt) работают.
Это говорит о том, что проблема, по крайней мере частично, связана с количеством аргументов. Однако я не совсем понимаю, что здесь происходит, и буду признателен за любую помощь.
Спасибо @Sweeper - есть ли что-нибудь более краткое, что я могу сделать, чтобы решить проблему, кроме «обертывания», которое я делаю в разделе «Временные решения» моего вопроса?
Вы можете объявить свой собственный «преобразователь функций», который превращает (Any..., String, String) -> Void в (Any) -> Void, но я бы сказал, что это также «обертка». Вы просто должны обернуть его.





Это еще один обходной путь, если вы хотите использовать только 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), вероятно, будет более понятным для людей, читающих код.
Это проблема с количеством (переменных И необязательных!) параметров в
(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.... Это не то же самое, что Any. Это не супертип Int или Any, поэтому LSP здесь не применяется, даже если проблема с необязательными параметрами волшебным образом решена.
Поэтому вам нужно каким-то образом обернуть его, если вы хотите передать его напрямую forEach. Вместо того, чтобы обернуть его только для Int, вы можете подумать о том, чтобы обернуть его для Any:
func print(_ x: Any) {
// need "Swift." here, otherwise infinite recursion
Swift.print(x)
}
Затем, благодаря LSP, вы можете передать это напрямую в forEach для коллекций любого типа элементов.
Хотя лично я бы не стал заморачиваться и просто использовал бы forEach { print($0) }.
«Это проблема с количеством (переменных И необязательных!) параметров в (Any,... String, String)?» Да