2

I have read all the posts I can find here about arrays of functions - great you can do it. I figured. But none of the posts show practically how to use them (at least not what I'm trying to do). Here's what I want - they can all take the same args, but that's not a requirement.

This article is close, and will allow me to loop through to execute each function (which meets the first goal).
https://stackoverflow.com/a/24447484/11114752

But... what if I want to execute a single function by reference? In other words, how to call just the referenced Arity2 function - for example:

// None of these work (with or without the parameter labels)
funcs.Arity2(n: 2, S: "Fred)    // value of type [MyFuncs] has no member .Arity2
funcs[Arity2](n: 2, S: "Fred")  // no exact matches to call in subscript
funcs[.Arity2](n: 2, S: "Fred") // Cannot call value of non-function type...

let fn = funcs.first(where: { a whole ton of permutations here to try to match Arity2 }) -- a whole lotta frustrating nope...

Help, please! Nothing I've tried works. The pre-compiler just goes in circles making suggestions that don't pan out and it will not compile.

EDIT: The reason for the array in the first place is that I'm going to have a quite a few functions, and I don't know what they all are in advance. Essentially, I want a plugin type of architecture. Where I can add to the list of functions (ideally within an extension of the class, but that's another problem..) and not change the processing loop that executes each function in order.

Michael Stone
  • 73
  • 1
  • 8
  • Does this answer your question? [How to access a Swift enum associated value outside of a switch statement](https://stackoverflow.com/questions/31359142/how-to-access-a-swift-enum-associated-value-outside-of-a-switch-statement) – pawello2222 Sep 03 '20 at 22:36
  • 2
    There's no need for the complexity of the answer you're following (unless you're doing something much more complicated than you're describing). Check the following answer to the same question (a lot of the answers on that page are dealing with pre-1.0 Swift): https://stackoverflow.com/a/26416417/97337 Write up an example of a couple of functions you're trying to call. How would you do this *without* an array? Just hard code them and show what you want to call, and we'll show how to turn that into an array. – Rob Napier Sep 03 '20 at 22:39
  • I tried the pawello2222's answer - it does not work right with functions, which is my frustration. – Michael Stone Sep 03 '20 at 22:56
  • @Rob - that's my alternative and what I'm working with now. First goal - execute all functions (in array order - first function first, second, ...). Second goal - access individually by name. So, looks like I'll need two arrays unless the named one also maintains the order they're declared (will need some testing). – Michael Stone Sep 03 '20 at 22:59
  • Testing confirms order appears to be maintained as desired – Michael Stone Sep 04 '20 at 15:51
  • @RobNapier - thank you for your suggestion. This has been a fascinating exercise in learning how Swift works. After confirming that order is maintained, I think I'm going with the simpler solution you mentioned here (https://stackoverflow.com/a/26416417/97337). The only thing missing there is the ability to dynamically append to the list of functions. Any ideas? funcs.append("function3", function3) produces error "Value of type {func type} as no member append. – Michael Stone Sep 04 '20 at 16:01
  • That syntax doesn't look correct. Is `funcs` a function, or a dictionary, or an array? If it's a function, then there's nothing to append. If it's a dictionary, then you don't append to dictionaries, you assign values (`funcs["function3"] = function3`). If it's an array, then that's just the wrong syntax and I'm not clear what the type of the array is. – Rob Napier Sep 04 '20 at 16:22
  • let dictionaryOfFunctions = [ "function1": function1, "function2": function2 ] func function1() { NSLog("function1") } func function2() { NSLog("function2") } func function3() { NSLog("function3") } dictionaryOfFunctions.append("function3", function3) // Error... no member append. EDIT: Ahh.... OK! Let me try that :) – Michael Stone Sep 04 '20 at 17:31
  • Yep - that did it. Sorry, I'm still fairly new with Swift. THANK YOU!!! – Michael Stone Sep 04 '20 at 17:37

2 Answers2

1

I assume you need something like

    _ = funcs.first {
        if case let MyFuncs.Arity2(f) = $0 {
            f(2, "Fred")
            return true
        }
        return false
    }
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • YES!! That's the magical incantation I was looking for - combining if, case and .first. Wow - gotta love Swift :). Given the complexity and the fact that I have to deal with a conditional, I'm rethinking my approach - but this answers the question - thank you. – Michael Stone Sep 04 '20 at 12:57
  • Rechecked: This does seem to work even if I only know the name – Michael Stone Sep 04 '20 at 15:38
0

It can be achieved in a much simpler way if you know the position of the function in the array.

Assuming you have:

func someFunc(n: Int, s: String) { 
    print("call \(n) \(s)")
}

var funcs = [MyFuncs.Arity2(someFunc)]

you can do:

if case .Arity2(let f) = funcs.first {
    f(2, "Fred")
}

By replacing funcs.first with funcs[i] you can access the i-th index (first make sure it does exist).

pawello2222
  • 46,897
  • 22
  • 145
  • 209
  • I like this. It compiles without complaint. However, I confirmed, in init(), I have: funcs.append(MyFuncs.Arity2(someFunc)) Also confirmed funcs contains Arity2 in debugger. But the if statement never fires (using either .first or [0]) – Michael Stone Sep 04 '20 at 15:15
  • 1
    @MichaelStone I assumed you know the position of your function in the array. – pawello2222 Sep 04 '20 at 15:19
  • `code` var lFn = Array(). ... init(){ lFn.append(MyFuncs.Arity2(someFunc)) }. ... suggested if block above never executes the function. But this would be great if it did! I know the name (Arity2) and the position (0), but neither funcs.first, nor funcs[0] seems to satisfy the condition. Although I may not know the position in my actual usage. I was wrong, position is 1, and that does work. So, I still need .first(where; ...) if I only know the name. – Michael Stone Sep 04 '20 at 15:22
  • @MichaelStone Yes, it only works if you know the position. Otherwise you need to search for the first matching signature as Asperi suggested. – pawello2222 Sep 04 '20 at 15:28
  • Yeah, again - unless I'm missing something, it only seems to work if I specify the correct position (eg: if Arity2 is really the first element, or if I specify the correct position in brackets). I have as in the example Arity0 as the first element now and Arity2 in the second to ensure I am testing the correct scenario. – Michael Stone Sep 04 '20 at 15:33
  • thank you for all your help and suggestions! This has been a fascinating exercise in learning how Swift works. – Michael Stone Sep 04 '20 at 16:02