Bad practice for a method to call itself? - swift

I've a question about good or bad practice.
I've created a function which will generate a random number. And if the random number is equal to the previous random number it should generate a new number. So my question is. Is it bad practice to call the same method from the method?
func getRandomNumber(){ //<-- Method name
let randomNumber = Int.random(in: 0..<allPlayers.count)
if lastRoundNumber == randomNumber{
getRandomNumber() //<-- Like this
}
print(randomNumber)
}
Or should I do this is another way? If yes, how?
So is it bad practice to call the same method from the current method like I've done in the code above? Thanks in advance.
If yes, why is it bad? And how can you do this to get a "better" code?

There is nothing wrong with having a function call itself. It’s called recursion. When not implemented properly, it can introduce some overhead, but sometimes it can be a very elegant solution.
That having been said, you might not want to do it like you have here. What if it guessed the same number three times before it got one that wasn’t equal to lastRoundNumber? You’d see four print statements for one new value. Do you really want that behavior? If you were going to implement getRandomNumber as a recursive function, at the very least I’d suggest inserting a return statement after it calls itself recursively, so that you don’t get print statements for the iterations where it ended up with the same value as lastRoundNumber.
That having been said, we often only reach for recursion (and the overhead that entails) when that implementation is appreciably more elegant or intuitive than the non-recursive rendition. But in this case, the non-recursive rendition is probably just as clear, and as such, we’d likely favor it over the recursive version. It might look like:
func getRandomNumber() {
guard allPlayers.count > 1 else { return }
var randomNumber: Int
repeat {
randomNumber = .random(in: 0..<allPlayers.count)
} while randomNumber == lastRoundNumber
print(randomNumber)
}
Note, I’m checking that you have more than one player to avoid the possibility of infinite loop.
But let's say there were 100 players. And let’s say you called this 100 times. Is it OK if it returned player 1, then player 2, then player 1 again, then player 2 again, repeating again and again, never returning any players 3 through 100. This is unlikely, but it’s possible. Is that OK?
Often we want to return all players, but in a random order. In that case, you’d “shuffle” the list, e.g.
let players = (0..<allPlayers.count).shuffled()
That will ensure that you have an array of integer values, shuffled into random order, but never repeating any given number. That provides randomness while also ensuring that each value is returned only once.
It just depends upon your desired behavior.

If you call a method from the same method, it is called recursion.
You can find here an explanation how recursion in swift works.
You should make sure that your method has an exit condition, so you are not stuck in your call.
Let's look at an example call of your method. lastRoundNumber is 1.
Your generated number is also 1. So it will call the method again. Then you will generate the number 2.
With print(randomNumber) you will get the following output:
2
1
It will happen, because the print-statement will be excuted, even if you call the method again.
So you need to rework your if statement to the following:
if lastRoundNumber == randomNumber{
getRandomNumber() //<-- Like this
} else {
print(randomNumber)
}
In this way, it will only print the last generated value

Related

Swift - understanding return types in recursion: when should return the function vs. just return on its own

This may be a little too general for this forum, but hoping someone can explain this in a way that makes sense to my brain. I've tried reading and researching and have found lots of examples - but I still don't understand the "why" which means that I am not understanding exactly how a program comes back from a function return.
Here is a very simple function I wrote that solves a puzzle using backwards recursion. It works well.
func solver(grid: [[Int]])->[[Int]] {
var returnGrid = constraintPropogation(grid: grid)
if contradictionCheck(grid: returnGrid) == false {
return returnGrid
} else {
if returnGrid.flatMap({$0}).filter({$0 == 3}).count == 0 {
print("SOLVED**********")
gridPrint(grid: returnGrid)
print()
stopFlag = true
stopAnswer = returnGrid
return returnGrid
} else {
let randStart = getRandomStart(grid: returnGrid)
returnGrid[randStart.x][randStart.y] = 0
solver(grid: returnGrid)
returnGrid[randStart.x][randStart.y] = 1
solver(grid: returnGrid)
}
}
if stopFlag == true {return stopAnswer}
return solver(grid: returnGrid)
}
My issue is understanding the returns. In the third line of the function if a contradiction check fails this means that we've gone down a path that would not be possible. Therefore we return. That makes sense. The second return in the middle of the function occurs when the puzzle is solved, so it makes sense to return there. But the last one at the end "return solver(grid: returnGrid)" is challenging to my understanding. Here we are returning but also calling this same function again. This is not going deeper into the potential solution path (that happens in the "else" section where the function is called). Why do we need to call the function again rather than just returning? What is happening under the hood? Does the return happen first where we "pop back up a level" and then we are calling the function again effectively one rung higher on the stack? When I write these words I realize that I have a vague understanding - but somehow it is not all clicking together for me.
Am at the point now that when I'm writing functions that involve recursion I just try both returning on own or returning and calling function again to see which one achieves what I want. But I'd really like to understand it rather than just guessing. If anyone had a simple explanation I would appreciate it.
Your function solver takes an array of arrays, and returns an array of arrays. Any call to return(something) returns that something to the caller.
Saying return(someArrayOfArrays) means you are done, and have a result.
Saying return(solver(someArrayOfArrays)) says "call this function again, passing in a new value. Return whatever the result is as the function result." The current call to solver() is done doing work, and passes its intermediate results to another call to the function. That's the recursion. You can think of this as nesting a function call inside a function call inside a function call, or stacking function calls on top of each other.
The call to solver(grid: returnGrid) does not make any sense. That is a recursive call, but you ignore the result. Thus, that call does nothing useful. If your remove that line, it won't make any difference to the outcome. That line is saying "Go do a bunch of work and find an answer for me, but I will throw away your answer". The compiler should give you a "function result ignored" warning at that line.
Beyond that, I can't tell what your code is doing. It appears to be modifying at least one global variable, "stopAnswer". That suggests it's not a pure recursive function.

How do for-in loops in swift work?

I've used them, I'm familiar with the code and read a number tutorials but I still don't understand exactly how they work in that I can't run through my head what I'm doing and ultimately what I want to achieve, as opposed to say an if statement which can be read in English quite well.
For-loops have always been something I've struggled with through lack of understanding, can someone offer some insight please?
The for-in loop performs a set of statements for each item in a range or collection. Swift also provides two range operators a..<b and a...b, as a shortcut for expressing a range of values.
// prints 1-10
for i in 1...10 {
print(i)
}
// This way has been removed in Swift 3 so use the above
for var i = 1; i <= 10; i+=1 {
print(i)
}
The for-in loop is used for iterations on numbers, items in an array or characters in a string.
//I want my var res to be equal to 10:
for var nb = 0 in 0..10 {
nb += 1
print("my nb is \(nb)"
}
To understand for loops, one needs to understand the need for repeating lines of code (an unrolled loop) for a bunch of sequential numbers or array elements, etc. A "for-loop" tells the processor to do most of the repetition for you, without the need for you to copy-paste all those nearly identical lines of code a whole bunch (maybe millions or billions) of times.
A "for in" loop lets you specify the range (of numbers or array elements, etc.) over which you want the repetition, so the code doesn't go on repeating forever.

How safe are swift collections when used with invalidated iterators / indices?

I'm not seeing a lot of info in the swift stdlib reference. For example, Dictionary says certain methods (like remove) will invalidate indices, but that's it.
For a language to call itself "safe", it needs a solution to the classic C++ footguns:
get pointer to element in a vector, then add more elements (pointer is now invalidated), now use pointer, crash
start iterating through a collection. while iterating, remove some elements (either before or after the current iterator position). continue iterating, crash.
(edit: in c++, you're lucky to crash - worse case is memory corruption)
I believe 1 is solved by swift because if a collection stores classes, taking a reference (e.g. strong pointer) to an element will increase the refcount. However, I don't know the answer for 2.
It would be super useful if there was a comparison of footguns in c++ that are/are not solved by swift.
EDIT, due to Robs answer:
It does appear that there's some undocumented snapshot-like behavior going on
with Dictionary and/or for loops. The iteration creates a snapshot / hidden
copy of it when it starts.
Which gives me both a big "WAT" and "cool, that's sort of safe, I guess", and "how expensive is this copy?".
I don't see this documented either in Generator or in for-loop.
The below code prints two logical snapshots of the dictionary. The first
snapshot is userInfo as it was at the start of the iteration loop, and does
not reflect any modifications made during the loop.
var userInfo: [String: String] = [
"first_name" : "Andrei",
"last_name" : "Puni",
"job_title" : "Mad scientist"
]
userInfo["added_one"] = "1" // can modify because it's var
print("first snapshot:")
var hijacked = false
for (key, value) in userInfo {
if !hijacked {
userInfo["added_two"] = "2" // doesn't error
userInfo.removeValueForKey("first_name") // doesn't error
hijacked = true
}
print("- \(key): \(value)")
}
userInfo["added_three"] = "3" // modify again
print("final snapshot:")
for (key, value) in userInfo {
print("- \(key): \(value)")
}
As you say, #1 is not an issue. You do not have a pointer to the object in Swift. You either have its value or a reference to it. If you have its value, then it's a copy. If you have a reference, then it's protected. So there's no issue here.
But let's consider the second and experiment, be surprised, and then stop being surprised.
var xs = [1,2,3,4]
for x in xs { // (1)
if x == 2 {
xs.removeAll() // (2)
}
print(x) // Prints "1\n2\n3\n\4\n"
}
xs // [] (3)
Wait, how does it print all the values when we blow away the values at (2). We are very surprised now.
But we shouldn't be. Swift arrays are values. The xs at (1) is a value. Nothing can ever change it. It's not "a pointer to memory that includes an array structure that contains 4 elements." It's the value [1,2,3,4]. At (2), we don't "remove all elements from the thing xs pointed to." We take the thing xs is, create an array that results if you remove all the elements (that would be [] in all cases), and then assign that new array to xs. Nothing bad happens.
So what does the documentation mean by "invalidates all indices?" It means exactly that. If we generated indices, they're no good anymore. Let's see:
var xs = [1,2,3,4]
for i in xs.indices {
if i == 2 {
xs.removeAll()
}
print(xs[i]) // Prints "1\n2\n" and then CRASH!!!
}
Once xs.removeAll() is called, there's no promise that the old result of xs.indices means anything anymore. You are not permitted to use those indices safely against the collection they came from.
"Invalidates indices" in Swift is not the same as C++'s "invalidates iterators." I'd call that pretty safe, except the fact that using collection indices is always a bit dangerous and so you should avoid indexing collections when you can help it; iterate them instead. Even if you need the indexes for some reason, use enumerate to get them without creating any of the danger of indexing.
(Side note, dict["key"] is not indexing into dict. Dictionaries are a little confusing because their key is not their index. Accessing dictionaries by their DictionaryIndex index is just as dangerous as accessing arrays by their Int index.)
Note also that the above doesn't apply to NSArray. If you modify NSArray while iterating it, you'll get a "mutated collection while iterating" error. I'm only discussing Swift data types.
EDIT: for-in is very explicit in how it works:
The generate() method is called on the collection expression to obtain a value of a generator type—that is, a type that conforms to the GeneratorType protocol. The program begins executing a loop by calling the next() method on the stream. If the value returned is not None, it is assigned to the item pattern, the program executes the statements, and then continues execution at the beginning of the loop. Otherwise, the program does not perform assignment or execute the statements, and it is finished executing the for-in statement.
The returned Generator is a struct and contains a collection value. You would not expect any changes to some other value to modify its behavior. Remember: [1,2,3] is no different than 4. They're both values. When you assign them, they make copies. So when you create a Generator over a collection value, you're going to snapshot that value, just like if I created a Generator over the number 4. (This raises an interesting problem, because Generators aren't really values, and so really shouldn't be structs. They should be classes. Swift stdlib has been fixing that. See the new AnyGenerator for instance. But they still contain an array value, and you would never expect changes to some other array value to impact them.)
See also "Structures and Enumerations Are Value Types" which goes into more detail on the importance of value types in Swift. Arrays are just structs.
Yes, that means there's logically copying. Swift has many optimizations to minimize actual copying when it's not needed. In your case, when you mutate the dictionary while it's being iterated, that will force a copy to happen. Mutation is cheap if you're the only consumer of a particular value's backing storage. But it's O(n) if you're not. (This is determined by the Swift builtin isUniquelyReferenced().) Long story short: Swift Collections are Copy-on-Write, and simply passing an array does not cause real memory to be allocated or copied.
You don't get COW for free. Your own structs are not COW. It's something that Swift does in stdlib. (See Mike Ash's great discussion of how you would recreate it.) Passing your own custom structs causes real copies to happen. That said, the majority of the memory in most structs is stored in collections, and those collections are COW, so the cost of copying structs is usually pretty small.
The book doesn't spend a lot of time drilling into value types in Swift (it explains it all; it just doesn't keep saying "hey, and this is what that implies"). On the other hand, it was the constant topic at WWDC. You may be interested particularly in Building Better Apps with Value Types in Swift which is all about this topic. I believe Swift in Practice also discussed it.
EDIT2:
#KarlP raises an interesting point in the comments below, and it's worth addressing. None of the value-safety promises we're discussing are related to for-in. They're based on Array. for-in makes no promises at all about what would happen if you mutated a collection while it is being iterated. That wouldn't even be meaningful. for-in doesn't "iterate over collections," it calls next() on Generators. So if your Generator becomes undefined if the collection is changed, then for-in will blow up because the Generator blew up.
That means that the following might be unsafe, depending on how strictly you read the spec:
func nukeFromOrbit<C: RangeReplaceableCollectionType>(var xs: C) {
var hijack = true
for x in xs {
if hijack {
xs.removeAll()
hijack = false
}
print(x)
}
}
And the compiler won't help you here. It'll work fine for all of the Swift collections. But if calling next() after mutation for your collection is undefined behavior, then this is undefined behavior.
My opinion is that it would be poor Swift to make a collection that allows its Generator to become undefined in this case. You could even argue that you've broken the Generator spec if you do (it offers no UB "out" unless the generator has been copied or has returned nil). So you could argue that the above code is totally within spec and your generator is broken. Those arguments tend to be a bit messy with a "spec" like Swift's which doesn't dive into all the corner cases.
Does this mean you can write unsafe code in Swift without getting a clear warning? Absolutely. But in the many cases that commonly cause real-world bugs, Swift's built-in behavior does the right thing. And in that, it is safer than some other options.

Flowgorithm: importing variables and modifying their values

My professor likes work to be turned in in Flowgorithm.
I can't figure out how to take the variable X=0 from the main method to the displayLoop method and then allow the displayLoop method to modify the stored value so it can be used as a countdown (or countup in this case).
Flowgorithm, like Java, uses pass-by-value for types like Internet, Real, etc...
To return one less, you can create a function and return the decremented value. So, maybe try something like this:
count = displayLoop(count)
The assignment sounds a tad strange. Is this a recursion assignment?

clearer explanation of function level scope for recursion

This is an example from the book 'Matlab for Neuroscientists'. I don't understand the order in which, or why, g gets assigned a new value after each recursion. Nor do I understand why "factorial2" is included in the final line of code.
here is a link to the text
Basically, I am asking for someone to re-word the authors explanation (circled in red) of how the function works, as if they were explaining the concept and processes to a 5-year old. I'm brand new to programming. I thought I understood how this worked from reading another book, but now this authors explanation is causing nothing but confusion. Many thanks to anyone who can help!!
A recursive method works by breaking a larger problem into smaller problems each time the method is called. This allows you to break what would be a difficult problem; a factorial summation, into a series of smaller problems.
Each recursive function has 2 parts:
1) The base case: The lowest value that we care about evaluating. Usually this goes to zero or one.
if (num == 1)
out = 1;
end
2) The general case: The general case is what we are going to call until we reach the base case. We call the function again, but this time with 1 less than the previous function started with. This allows us to work our way towards the base case.
out = num + factorial(num-1);
This statement means that we are going to firstly call the function with 1 less than what this function with; we started with three, the next call starts with two, the call after that starts with 1 (Which triggers our base case!)
Once our base case is reached, the methods "recurse-out". This means they bounce backwards, back into the function that called it, bringing all the data from the functions below it!It is at this point that our summation actually occurs.
Once the original function is reached, we have our final summation.
For example, let's say you want the summation of the first 3 integers.
The first recursive call is passed the number 3.
function [out] = factorial(num)
%//Base case
if (num == 1)
out = 1;
end
%//General case
out = num + factorial(num-1);
Walking through the function calls:
factorial(3); //Initial function call
//Becomes..
factorial(1) + factorial(2) + factorial(3) = returned value
This gives us a result of 6!