How to use an array of functions in Swift - arrays

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.

I assume you need something like
_ = funcs.first {
if case let MyFuncs.Arity2(f) = $0 {
f(2, "Fred")
return true
}
return false
}

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).

Related

Go Ranged Loop Scope and Indirect Function

Can someone help enlighten me with the "why" aspect of the following surprise I had with golang loop scoping and temporary functions? Following is an excerpt reduction from some more-complex code:
package main
import ( "fmt" )
type Caller struct {
call func()
}
func printer(val int) {
fmt.Printf("the value is %v\n", val)
}
func main () {
values := []int{1,2,3}
var callers []Caller
for _,val := range values {
call := func() { printer(val) }
callers = append(callers, Caller{call})
}
for _, caller := range callers {
caller.call()
}
}
which produces the (to me) surprising outcome:
the value is 3
the value is 3
the value is 3
If I alter that code by changing the range values loop body to this:
theVal := val
call := func() { printer(theVal) }
callers = append(callers, Caller{call})
then we get the originally hoped-for result:
the value is 1
the value is 2
the value is 3
Fundamentally, one works and the other doesn't - I'm clear on that and can try to just memorize the idiom. I'm hoping for more understanding, perhaps a little life lesson in golang. What is it about scoping rules and deferred execution that means the loop variable maintains the final valid value and is submitted into every one of the temporary functions built during the loop? Why isn't the value "val" dropped into the dynamically-built func "call"? I suspect that I am confused about something fundamental. Even seeing a working version, I am not certain I will be able to avoid a trap like this in the future. If you have advice for "why" the iterated value acts like that, I'd love to hear it (and thank you in advance).
This will work for you as well. Here's the link from the FAQ.
package main
import (
"fmt"
)
type Caller struct {
call func()
}
func printer(val int) {
fmt.Printf("the value is %v\n", val)
}
func main() {
values := []int{1, 2, 3}
var callers []Caller
for _, val := range values {
var call func()
func(v int) {
call = func() {
printer(v)
}
}(val)
callers = append(callers, Caller{call})
}
for _, caller := range callers {
caller.call()
}
}
An alternate method would be to bind the current value of val to each closure as it is launched, you can store it in a new variable and then use it (the method you have done to solve it).
Just adding a val := val inside your loop would be enough to make it work (see playground)
As you mentioned:
This is because each iteration of the loop uses the same instance of the variable v, so each closure shares that single variable.
But... That might change.
The discussion "redefining for loop variable semantics" is precisely about that:
We have talked for a long time about redefining these semantics, to make loop variables per-iteration instead of per-loop.
That is, the change would effectively be to add an implicit "x := x" at the start of every loop body for each iteration variable x, just like people do manually today.
Making this change would remove the bugs from the programs above.
Possibly for Go 1.30, with lots of warning in the meantime)
To make the breakage completely user controlled, the way the rollout would work is to change the semantics based on the go line in each package’s go.mod file, the same line we already use for enabling language features (you can only use generics in packages whose go.mod says “go 1.18” or later).
Just this once, we would use the line for changing semantics instead of for adding a feature or removing a feature.
This is still actively discussed though.

Accessing an array inside an array

I am new to coding in scala and I am curious about something and it has been hard to find an answer for online. I have this array that takes multiple arguments of different types (:Any)
val arguments= Array("Monday",10,20,Array("test","test2"), if(4 == 4){ "true"})
I iterated and printed the content inside of it. Everything prints properly except the Array at index 3. I get the object memory address I believe, which is understandable-- same thing with Java would happen. But I am curious, how would you access it?
I tried saving the value of arguments(3) in an array (val arr:Array[String] = arguments(3)) but it didn't work because there is a type mismatch (any != Array[String])
Any tips? It might be a gap in my understanding of functional programming.
What you are iterating through is an Array[Any], so you are able to perform functions that are available to an Any type. You can access the items in your array using pattern matching, which uses the unapply methods under the hood to see if it can turn your Any into something more specific:
val arguments= Array("Monday",10,20,Array("test","test2"), if(4 == 4){ "true"})
arguments foreach { arg =>
arg match {
case a:Array[String] => println(s"This is the array: ${a.mkString(",")}, and I can do array functions ${a.contains("test")}")
case _ => println(s"Otherwise I have this: $arg")
}
}
// stdout:
// Otherwise I have this: Monday
// Otherwise I have this: 10
// Otherwise I have this: 20
// This is the array: test,test2, and I can do array functions true
// Otherwise I have this: true

Go: How to get length of slice in function?

I've got a function to which I want to feed different kinds of slices after which I want to loop over them and print their contents. The following code works:
func plot(data interface{}){
fmt.Println(data)
//fmt.Println(len(data))
}
func main() {
l := []int{1, 4, 3}
plot(l)
}
But when I uncomment the line in which I print the length of the slice, I get an error saying invalid argument data (type interface {}) for len.
Any idea how I would be able to get the length of the slice so that I can loop over it?
You should try to avoid using interface{} whenever possible. What you want to do can be done with reflection, but reflection is a necessary evil. It is really good for marshalling, but should be used sparingly. If you still want to use reflect, you can do something like this:
func plot(data interface{}) {
s := reflect.ValueOf(data)
if s.Kind() != reflect.Slice {
panic("plot() given a non-slice type")
}
for i := 0; i < s.Len(); i++ {
v := s.Index(i)
...
}
}
Even after doing this, v is a reflect.Value. You will then need to somehow convert that to something useful. Luckily, Value has many methods that can be used to convert it. In this case, v.Int() would return the value as an int64.
As hinted in comments you would have to use reflection to do this, something like the following:
var sliceLen int
switch reflect.TypeOf(data).Kind() {
case reflect.Slice:
sliceLen = s.Len();
default:
//error here, unexpected
}
}
Although go provides reflection to do these little tricks when you need to (as well as many other uses), it is often better to avoid wherever possible to maintain compiler type safety and performance, consider the pros/cons of having separate functions for different data types over this approach

Check validity of element in an array of objects and then retrieve that object and its index?

I have a simple array of objects:
var cars: [Car] = [Car(ID: "010", name: "car1"), Car(ID: "230", name: "car2"), Car(ID: "350", name: "car3")]
The user will use a UITextfield to enter an ID.
I will then have to check the array to see if there is an object that has this exact ID, if such an object exists I need to retrieve the object and the index where it is located.
In addition I need to update a flag variable to know whether the user entered a correct or incorrect number. As I will use this to change other aspects of the view controller (such as lay-out).
I currently have this function that receives the ID as parameter and then returns a tuple for another function. The carObject and indexOfCar are global variables that have to be updated:
func checkIdMatch(IdInput: String) -> (Bool, Car?) {
var returnObject: Car?
var tuple = (false, returnObject)
for (index, car) in cars.enumerated() {
if car.carId == IdInput {
returnObject = car
tuple = (true, car)
carObject = car
indexOfCar = index
flag = 2 //correct ID
} else {
flag = 1 //wrong ID
}
}
self.tableView?.reloadData()
return tuple
}
I tried to use a where statement after the for()-statement, but then I couldn't update the flags correctly. What am I doing wrong?
Any help is immensely appreciated!
There's a lot going on here, I'm going to try to address everything, one at a time:
In your code, you never break your loop early when you find a match.
There are obvious performance implications
Has the (probably undesired) side effect that flag keeps getting set over and over again. Thus, flag will only be 2 if the correct ID is found last in the array (so that there's no next element for which it'll be set back to 1).
Your flag is a raw integer. It should probably be an enumeration.
Your cars array has an unnecessary type annotation : [Car] can (and should) be left to the compiler to infer.
Your (Bool, Car?) tuple is superfluous. It's a clever idea in feneral: pairing a data value with a data value (such as a Car instance, in this case) with a Bool value that represents whether or not the data value is non-null. Apple liked this design so much, in fact, that this is exactly how Optional works! You just need to return Car?, which can either be a valid instance, or nil.
Your returnObject variable is never used, and ironically, never returned.
Your function called checkIdMatch(IdInput:) not only checks if an ID matches, but also removes it from the array, changes global state, and refreshes your table view. This doesn't sound like "checking" to me!
Your function name, and its parameter use Id, whereas your Car uses ID. Inconsistent.
Your function's parameter doesn't need the Input suffix. We know that parameters are inputs.
carObject and indexOfCar probably shouldn't be globals, but I don't have enough context to be able to tell. In any case, wouldn't indexOfCar always be inaccurate? You're storing the index where the Car is, but then you immediately remove the car, so the index is now wrong.
You use copious amounts of so called "side effects". This is when a function has an effect apart from just processing its parameters into some return value. This is unavoidable in many cases, but having lots of side effects make code really hard to work with, hard to maintain, and hard to extend. Wherever possible, try to minimize side effects and reliance on global state. Ideally, you should try to write "pure" functions. Those that have no effect beyond returning a result. They're much, much easier to work with.
Here's my first attempt:
struct Car {
let id: String
let name: String
}
enum Flag: Int { //TODO: Name me!
case original
case noMatch
case match
}
var flag = Flag.noMatch;
var cars = [Car(id: "010", name: "car1"), Car(id: "230", name: "car2"), Car(id: "350", name: "car3")]
func removeCar(ID: String) -> Car? {
guard let index = cars.index(where: {$0.id == id}) else {
flag = .noMatch //TODO: side effects make kittens cry
return nil
}
flag = .match //TODO: side effects make kittens cry
return cars.remove(at: index)
}
print("Before:\r\n\(cars)\r\n\r\n")
print("Removed:\r\n\(removeCar(id: "350"))\r\n\r\n")
print("After:\r\n\(cars)\r\n\r\n")
You can try it online, here.

Why do I need a '<' overload for an Array class?

I'm trying to add functionality to an Array class.
So I attempted to add a sort() similar to Ruby's lexicon.
For this purpose I chose the name 'ricSort()' if deference to Swift's sort().
But the compiler says it can't find an overload for '<', albeit the 'sort({$0, $1}' by
itself works okay.
Why?
var myArray:Array = [5,4,3,2,1]
myArray.sort({$0 < $1}) <-- [1, 2, 3, 4, 5]
myArray.ricSort() <-- this doesn't work.
Here's a solution that is close to what you are looking for, followed by a discussion.
var a:Int[] = [5,4,3,2,1]
extension Array {
func ricSort(fn: (lhs: T, rhs: T) -> Bool) -> T[] {
let tempCopy = self.copy()
tempCopy.sort(fn)
return tempCopy
}
}
var b = a.ricSort(<) // [1, 2, 3, 4, 5]
There are two problems with the original code. The first, a fairly simple mistake, is that Array.sort returns no value whatsoever (represented as () which is called void or Unit in some other languages). So your function, which ends with return self.sort({$0 < $1}) doesn't actually return anything, which I believe is contrary to your intention. So that's why it needs to return tempCopy instead of return self.sort(...).
This version, unlike yours, makes a copy of the array to mutate, and returns that instead. You could easily change it to make it mutate itself (the first version of the post did this if you check the edit history). Some people argue that sort's behavior (mutating the array, instead of returning a new one) is undesirable. This behavior has been debated on some of the Apple developer lists. See http://blog.human-friendly.com/swift-arrays-the-bugs-the-bad-and-the-ugly-incomplete
The other problem is that the compiler does not have enough information to generate the code that would implement ricSort, which is why you are getting the type error. It sounds like you are wondering why it is able to work when you use myArray.sort but not when you try to execute the same code inside a function on the Array.
The reason is because you told the compiler why myArray consists of:
var myArray:Array = [5,4,3,2,1]
This is shorthand for
var myArray: Array<Int> = [5,4,3,2,1]
In other words, the compiler inferred that the myArray consists of Int, and it so happens that Int conforms to the Comparable Protocol that supplies the < operator (see: https://developer.apple.com/library/prerelease/ios/documentation/General/Reference/SwiftStandardLibraryReference/Comparable.html#//apple_ref/swift/intf/Comparable)[1]. From the docs, you can see that < has the following signature:
#infix func < (lhs: Self, rhs: Self) -> Bool
Depending on what languages you have a background in, it may surprise you that < is defined in terms of the language, rather than just being a built in operator. But if you think about it, < is just a function that takes two arguments and returns true or false. The #infix means that it can appear between its two functions, so you don't have to write < 1 2.
(The type "Self" here means, "whatever the type is that this protocol implements," see Protocol Associated Type Declaration in https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/swift_programming_language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-XID_597)
Compare this to the signature of Array.sort: isOrderedBefore: (T, T) -> Bool
That is the generic signature. By the time the compiler is working on this line of code, it knows that the real signature is isOrderedBefore: (Int, Int) -> Bool
The compiler's job is now simple, it just has to figure out, is there a function named < that matches the expected signature, namely, one that takes two values of type Int and returns a Bool. Obviously < does match the signature here, so the compiler allows the function to be used here. It has enough information to guarantee that < will work for all values in the array. This is in contrast to a dynamic language, which cannot anticipate this. You have to actually attempt to perform the sort in order to learn if the types can actually be sorted. Some dynamic languages, like JavaScript, will make every possible attempt to continue without failing, so that expressions such as 0 < "1" evaluate correctly, while others, such as Python and Ruby, will throw an exception. Swift does neither: it prevents you from running the program, until you fixed the bug in your code.
So, why doesn't ricSort work? Because there is no type information for it to work with until you have created an instance of a particular type. It cannot infer whether the ricSort will be correct or not.
For example, suppose instead of myArray, I had this:
enum Color {
case Red, Orange, Yellow, Green, Blue, Indigo, Violet
}
var myColors = [Color.Red, Color.Blue, Color.Green]
var sortedColors = myColors.ricSort() // Kaboom!
In that case, myColors.ricSort would fail based on a type error, because < hasn't been defined for the Color enumeration. This can happen in dynamic languages, but is never supposed to happen in languages with sophisticated type systems.
Can I still use myColors.sort? Sure. I just need to define a function that takes two colors and returns then in some order that makes sense for my domain (EM wavelength? Alphabetical order? Favorite color?):
func colorComesBefore(lhs: Color, rhs: Color) -> Bool { ... }
Then, I can pass that in: myColors.sort(colorComesBefore)
This shows, hopefully, that in order to make ricSort work, we need to construct it in such a way that its definition guarantees that when it is compiled, it can be shown to be correct, without having to run it or write unit tests.
Hopefully that explains the solution. Some proposed modifications to the Swift language may make this less painful in the future. In particular creating parameterized extensions should help.
The reason you are getting an error is that the compiler cannot guarantee that the type stored in the Array can be compared with the < operator.
You can see the same sort closure on an array whose type can be compared using < like an Int:
var list = [3,1,2]
list.sort {$0 < $1}
But you will get an error if you try to use a type that cannot be compared with <:
var URL1 = NSURL()
var URL2 = NSURL()
var list = [URL1, URL2]
list.sort {$0 < $1} // error
Especially with all the syntax you can leave out in Swift, I don't see a reason to define a method for this. The following is valid and works as expected:
list.sort(<)
You can do this because < actually defines a function that takes two Ints and returns a Bool just like the sort method is expecting.

Resources