Можно ли вызвать переменную asAsFunction (), которая является Any? - PullRequest
0 голосов
/ 20 октября 2019

Недавно я столкнулся с необходимостью создания массива функций. К сожалению, язык Swift не предоставляет тип верхнего уровня для функций, но вместо этого они должны быть объявлены своей конкретной подписью (...)->(...). Поэтому я попытался написать оболочку, которая может содержать любую функцию, а затем специализировать ее для хранения только замыканий, имеющих Void возвращаемый тип и любое количество аргументов.

struct AnyFunction {
    let function: Any
    init?(_ object: Any){
        switch String(describing: object){
            case let function where function == "(Function)":
                self.function = object
            default:
                return nil
        }
    }
    func callAsFunction(){
        self.function()
    }
}

Когда я прогрессировал, я обнаружил, что это не тривиально и, возможно, требует некоторых хаков с самоанализом, но мне не удалось найти решение, несмотря на мои попытки. Сообщение от компилятора:

error: cannot call value of non-function type 'Any'

Итак, как бы вы сделали этот трюк, чтобы уточнить, чтобы определить объект, который может содержать любой функциональный тип?

Уточнение: что я бы предпочел, этоопределяя что-то вроде:

typealias SelfSufficientClosure = (...)->Void
var a, b, c = 0
let funcs = FunctionSequence
    .init(with: {print("Hi!")}, {a in a + 3}, {b in b + 3}, { a,b in c = a + b})
for f in funcs { f() }

print([a, b, c])
//outputs 
//"Hi"
//3, 3, 6

PS Этот вопрос имеет отношение к этому ( Любой или проблема с последовательностью функций )

Ответы [ 2 ]

1 голос
/ 20 октября 2019

Функция - это отображение входов на выходы. В ваших примерах ваши входы недействительны (нет входов), а ваши выходы также недействительны (нет выходов). Так что этот тип функции в точности равен () -> Void.

. Мы можем сказать, что это правильный тип, потому что вы его называете:

for f in funcs { f() }

Вы ожидаете, что f будет функцией, котораяне принимает входные данные и не возвращает никаких выходных данных, что в точности соответствует определению () -> Void. Мы можем получить именно тот ввод и вывод, который вы ожидаете, используя этот тип (и очистив несколько синтаксических ошибок):

var a = 0, b = 0, c = 0

let funcs: [() -> Void] = [{print("Hi!")}, {a = a + 3}, { b = b + 3}, { c = a + b}]
for f in funcs { f() }

print(a, b, c, separator: ",")
//outputs
//Hi!
//3, 3, 6

Когда вы пишете замыкание типа {a in a + 3}, это не означает «захват»a "(что, я полагаю, вы ожидаете). Это означает «Это замыкание, которое принимает параметр, который будет называться a (полностью не связанный с глобальной переменной с тем же именем), и возвращает это значение плюс 3.»Если это то, что вы имели в виду, то когда вы вызывали f(), вам нужно было бы что-то передать и сделать что-то с возвращаемым значением.

0 голосов
/ 20 октября 2019

Вы можете использовать enum для помещения различных функций в массив, а затем извлекать функции с помощью переключателя.

 enum MyFuncs {
        case Arity0 ( Void -> Void )
        case Arity2 ( (Int, String) -> Void)
    }

    func someFunc(n:Int, S:String) { }
    func boringFunc() {}
    var funcs = Array<MyFuncs>()
    funcs.append(MyFuncs.Arity0(boringFunc))
    funcs.append( MyFuncs.Arity2(someFunc))

    for f in funcs {
        switch f {
        case let .Arity0(f):
            f()  // call the function with no arguments
        case let .Arity2(f):
            f(2,"fred") // call the function with two args
        }
    }
...