Swift for..in and for loop - swift

Why does for loop require var while for..in does not allow use of var?
for loop
for var index = 0; index < 10; i++ {
}
for..in loop
for index in "test" {
}
instead of:
for var index in "test" {
}

The Swift documentation sums it up pretty nicely:
index is a constant whose value is automatically set at the start of each iteration of the loop. As such, it does not have to be declared before it is used. It is implicitly declared simply by its inclusion in the loop declaration, without the need for a let declaration keyword.
In other words, the variable used in a for/in loop can only be a constant; thus, there's really no need to require that let be used.
The variable(s) used in a "traditional" for loop, however, can be variables or constants, so var or let is required. (Generally, they will be variables, but it is possible to use a constant in a for loop.) They can also be declared outside of the for loop (i.e., before it) and still used in the for loop. Because of this flexibility, you are required to declare it as a constant or variable.

The compiler expands for x in 0..<5 to the following:
var g = (0..<5).generate() {
while let x = g.next() {
// Use x in loop body
}
Every time around the loop, x is a freshly declared variable, the value of unwrapping the next result from calling next on the generator.
Now, that while can be rewritten in this fashion:
while var x = g.next() {
// use x here
}
I guess for this reason, for...in doesn't support the var syntax for declaring the loop counter so that it doesn't give you the wrong impression that x is mutable.

Related

Redeclaring variables in functions

Why does redeclaring output inside this if-statement not generate an error?
let output = 0
if counter < message.count {
let output = counter //This should throw an error, right?
counter += 1
} ...
The scope inside the if-statement knows about output as proven here, when trying to change the value of output instead of re-declaring it:
let output = 0
if counter < message.count {
output = counter //ERROR: Cannot assign to value: 'output' is a 'let' constant
counter += 1
} ...
There is no error because it is perfectly legal to declare a variable inside a closure with the same name of a variable which has been declared outside of the closure. It shadows the "outside declared variable".
In case your sample code is inside a class you could still access the "outside declared variable" using self:
class Foo {
let output = 0
func baa() {
let output = 1
print(output)
print(self.output)
}
}
Using this:
let foo = Foo()
foo.baa()
prints:
1
0
let output = counter is declaring a new variable (output) in the scope of the if statement, which has nothing to do with the output variable declared outside.
Edit
The code extract below illustrates that the output variables are not the same, despite their name. The value that gets changed is the one of the local output variable, not the one outside.
var message = [String]()
let output = 2
var counter = -1
if counter < message.count {
let output = counter //This should throw an error, right?
print("local output value is:\(output)") // here the local output value is -1 not 2.
counter += 1
}
The first example is a case of variable shadowing. This occurs when the code has multiple scope blocks. In the first example:
let output = 0 //outer scope block
if counter < message.count {
let output = counter //inner scope block
counter += 1
} ...
In the inner scope block, a new 'output' constant has been declared with the 'let' keyword. That output constant is only valid within that 'if' block. It happens to use the same name as the one declared above the "if" statement. This is variable shadowing.
For the second example:
let output = 0
if counter < message.count {
output = counter //ERROR: Cannot assign to value: 'output' is a 'let' constant
counter += 1
} ...
The error is occurring because defining something with 'let' makes it a constant. It cannot be changed. In this case there is only one 'output' constant. It was declared above the if statement and it is a constant. So, it cannot be changed once it has been assigned a value.
So, in the first example, there are 2 "output" constants, one of which is only valid within the "if" statement. In the second example, there is only 1 "output" constant.
Curly braces constitute a scope. In an inner scope, it is always legal to overshadow a variable name from an outer scope.
let output = // ... [A]
[class, func, if, for, do, etc.] {
let output = // ... [B, overshadows A]
// *
}
You've done a self-limiting thing, in one sense, in that code at the * point may now be unable to refer to output A; it is overshadowed by output B. But it isn't illegal. On the contrary, this is an important thing to be able to do, and to disallow it would be silly.

Swift Hoisting? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
As everyone knows, JavaScript "Hoists" the variables to the top of the file or scope. But from my understanding, using let is the same as var only let is confined to the scope it is defined in.
Being a compiled language instead of an interpreted, can we assume Swift does NOT do this?
For example:
x = 10
var y = x + 10
var x
can we assume Swift does NOT do this?
You can assume whatever you want, but no matter the programming language, your absolute best bet it to just try it and find out. If you would have pasted your sample code into a Playground or in any Swift-capable IDE, or just tried running it through the command line, you'd quickly find out that this simply does not work.
Your question is somewhat confusing, but I think I can address all or at least most of your questions.
x = 10
var y = x + 10
var x
Assuming there is no other code to go with your original sample, it simply does not compile. All three lines have a problem.
The first two lines complain about the use of an unresolved identified 'x'. In English, this means Swift can't figure out what variable you're talking about. Variables in Swift must be declared before they are used, so the declaration on line three doesn't help the next two lines.
The third line complains that it can't figure out what type x should be. "Type annotation missing in pattern". In some cases, Swift can figure out what type our variable should be. For example, with var x = 10, Swift can figure out that x's type should be Int. If we want something else, we must specify. But if we aren't assigning a value in the declaration, Swift has no idea and must be told: var x: Int?
What about the case where x exists at a different scope?
Well, Swift allows variable shadowing. That is to say, a variable declared at one scope hides a variable declared at another scope.
So, for example:
class Foo {
let x = 10
func foo(value: Int) -> Int {
let a = value * self.x
let x = 10
return a * x
}
}
Now we can use some x before we've declared a locally scoped x, but these are different variables. Also, perhaps most importantly, notice the self. prepended to x here. It's required. Without it, Swift will refuse to compile this code and will complain: "Use of local variable 'x' before its declaration."
However, within functions of classes is not the only place we can shadow variables. We can also do it within if blocks (among other places) which is where things can get a little more confusing. Consider this:
class Foo {
let x = 10
func foo(value: Int) -> Int {
print(x)
if x > 3 {
let x = 2
print(x)
}
return x
}
}
Here, we've used x twice before declaring it. And we didn't have to make use of the self. and it doesn't complain and compiles perfectly fine. But it's important to note that outside the if block (including the x > 3 conditional) the x we're referencing is the instance variable, but inside the if block, we've created a new variable called x which shadows the instance variable. We can also create the same sort of shadowing by using if let and if var constructs (but not guard let).
The result of calling this function will be that the value 10 is printed, we enter the if block, the value of 2 is printed, then we exit the if block and the value of 10 is returned.
Now let's through var into the mix here. First, if you aren't already aware, you should start by reading this which explains the difference between let and var (one is a constant, the other is not).
Let's combine let vs var with scoping and variable shadowing to see how it effects things.
class Foo {
let x = 10
func foo(value: Int) -> Int {
print(x)
if x > 3 {
var x = 2
while x < 10 {
print(x)
x += 3
}
return x
}
return x
}
}
Okay, so this is the same as before but with a slightly more complicated bit within the if block.
Here, our locally-scoped variable is declared as a var while our instance variable remains a constant let. We cannot modify the instance variable, but we can modify the local variable. And we do so, on each iteration of the while loop.
But importantly, this variable is an entirely different variable from the instance variable. It might as well have a completely different name (and in practice, it basically always should have a different name). So modifying our local variable x doesn't change anything about our more broadly scoped instance variable x. They're different variables that reside in different memory locations. And once a variable is declared as a let or a var, that variable cannot be changed to the other.
'let' and 'var' do not have a difference in scope.
Global variables are variables that are defined outside of any
function, method, closure, or type context. Local variables are
variables that are defined within a function, method, or closure
context.

TestComplete/JScript- Using a variable in an object path

I am using TestComplete with JScript testing a webpage that has elements that I declare as a variable to make it easier to test the element later. They all have a path like:
var check1 = Window.Panel(1).Panel(2).Panel(0).Panel(0).Panel(0).Panel(0).Panel(1).Panel(0).Label(0).Checkbox(0)
The elements are dynamic, so there is no telling how many there are when the test is run. I was hoping there was some way to loop through and declare the elements, but it would involve declaring the element like this:
var check1 = Window.Panel(1).Panel(2).Panel(0).Panel(0).Panel(0).Panel(0).Panel(1).Panel(0).Label(x).Checkbox(0)
where x is the counter variable. The problem is that TestComplete sees this as a literal path and does not recognize x as a variable.
Is there any way to do this with TestComplete using JScript? Or convert a string to an object? I think I can work with that, too.
My guess is that since you store the reference in variable check1, the variable x is updated but the x in variable check1 still holds it's original value (1).
Workaround
Keep the first part of the path static in the variable, then update x and assign it to the label.
var path = Window.Panel(1).Panel(2).Panel(0).Panel(0).Panel(0).Panel(0).Panel(1).Panel(0);
// path to the Checkbox
path.Label(x).Checkbox(0);
// or if you want to loop over it
for (var x = 0, len = 8; i < len; x += 1) {
if (path.Label(x).Checkbox(0).value === 'something') {
console.log('hooray!');
}
}

How Swift captures mutable and immutable struct

I'm confused about how Swift captures mutable and immutable struct
My code downloads images asynchronously, and after the download finish, the completion closure will be called.
In the following code, It prints captured index value. The first code will print only 17. But the second code will print 0 1 2 ... 16. ( sponsorClass.count == 17 )
The firs code is
var index = 0
for sponsor in sponsorClass{
var image = AsynchronousLoadImage( sponsor.imageURL ){ loadedImage in
println("\(index)") //print 17
}
index++
}
and the second code is
var index = 0
for sponsor in sponsorClass{
let tempIndex = index
var image = AsynchronousLoadImage( sponsor.imageURL ){ loadedImage in
println("\(tempIndex)") //print 0,1,2,..,16
}
index++
}
You must think of a var or let statement as creating a variable (or constant) when the statement is executed in order to understand what's going on. The statement creates a new variable (or constant) each time it's executed.
In your first example, you create a variable named index once, before the loop starts. Each closure (you create 17 of them) “closes over” that same index variable. This means that each closure shares the index variable with the outer scope (the block that defines index) and all the other closures. There's only one “copy” of index, so when you modify it in your loop, each closure sees the modification (when the closure eventually runs).
In your second example, you create a new variable named tempIndex on each loop iteration. These 17 variables are independent of each other. Each closure closes over the variable that was created in the same loop iteration as the closure. That is, the closure created while index == 0 closes over the tempIndex that was also created while index == 0, and the closure created while index == 1 closes over the tempIndex that was created while index == 1, and so on. Thus you create 17 independent variables (each named tempIndex), and 17 closures, and each closure closes over a separate variable. Since you never modify any of the 17 tempIndex variables after you create them, each closure sees the original value assigned to its corresponding variable.
Simply put:
Let = immutable var (constant) - value CAN'T be changed.
Var = mutable ( variable) - value CAN be changed.

Assigning last array element to a variable in Swift

I have this very simple line of code
var dblArray : [Double] = [0.01]
var x = dblArray.last
println(x * x)
The '.last' module returns the last element of the array, which is 0.01. However, based on the playground assistant view, it shows that the actual assignment to var x is (Some 0.01). And doing a println will lead to "Optional 0.01"
What I'm hoping to accomplish is merely capturing the value of the last element and placing it in x.
What am I doing wrong here?
I'm pretty certain .last would have to be an optional, if only to handle the edge case of an empty array, where .last would make no sense as a "solid" value.
In any case, if you're sure the array won't be empty, just unwrap the value. If you're not sure then you'll need to check intelligently such as with:
var x = 0
if let junk = dblArray.last {
x = junk
}
I think that's the correct syntax, I don't have my Mac with me at the moment, but it should hopefully be close enough to show the concept.