Как передать функцию печати в качестве аргумента forEach?

Функция 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) работают.

Это говорит о том, что проблема, по крайней мере частично, связана с количеством аргументов. Однако я не совсем понимаю, что здесь происходит, и буду признателен за любую помощь.

«Это проблема с количеством (переменных И необязательных!) параметров в (Any,... String, String)?» Да

Sweeper 09.05.2023 11:22

Спасибо @Sweeper - есть ли что-нибудь более краткое, что я могу сделать, чтобы решить проблему, кроме «обертывания», которое я делаю в разделе «Временные решения» моего вопроса?

R u c k s a c k 09.05.2023 11:27

Вы можете объявить свой собственный «преобразователь функций», который превращает (Any..., String, String) -> Void в (Any) -> Void, но я бы сказал, что это также «обертка». Вы просто должны обернуть его.

Sweeper 09.05.2023 11:30
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Это еще один обходной путь, если вы хотите использовать только 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) }.

Другие вопросы по теме