AppleScript_ The Definitive Guide - Matt Neuburg [153]
repeat with x in {1, 2, 3}
display dialog x -- 1, 2, 3
end repeat
In that example, the reference is implicitly dereferenced, and the value of each item is retrieved from the list. But consider what happens when the reference is not implicitly dereferenced. For instance, you might apply to variableName the equality or inequality operator:
repeat with x in {1, 2, 3}
if x = 2 then
display dialog "2"
end if
end repeat
The dialog never appears! That's because x is never 2. The second time through the loop, x is a reference to item 2 of {1, 2, 3}; that's not the same thing as the integer 2, and AppleScript doesn't implicitly dereference the reference. Clearly your script will misbehave if you're unprepared. The solution is to dereference explicitly:
repeat with x in {1, 2, 3}
if contents of x = 2 then
display dialog "2"
end if
end repeat
Here's another example; we'll retrieve each value and store it somewhere else:
set L1 to {1, 2, 3}
set L2 to {}
repeat with x in L1
set end of L2 to x
end repeat
What do you think L2 is after that? If you said {1, 2, 3}, you're wrong; it's this:
L2 -- {item 1 of {1, 2, 3}, item 2 of {1, 2, 3}, item 3 of {1, 2, 3}}
L1 is a list of values; L2 is a list of references. If you want L2 to end up identical to L1, you must dereference each reference:
set L1 to {1, 2, 3}
set L2 to {}
repeat with x in L1
set end of L2 to contents of x
end repeat
L2 -- {1, 2, 3}
A powerful consequence of the fact that variableName is a reference to an item of a list is that you can use it to assign back into the original list:
set L to {1, 2, 3}
repeat with x in L
set contents of x to item x of {"Mannie", "Moe", "Jack"}
end repeat
L -- {"Mannie", "Moe", "Jack"}
We can take advantage of this technique to rewrite the return-by-reference example from the end of Chapter 12 in a different way:
on findInList(what, L)
repeat with anItem in L
if contents of anItem is what then
return anItem
end if
end repeat
return
end findInList
local pep
set pep to {"Mannie", "Moe", "Jack"}
set contents of findInList("Moe", pep) to "Larry"
pep -- {"Mannie", "Larry", "Jack"}
On the whole, you should probably not make more radical alterations to the list during the course of a repetition, but it is not unsafe to do so. If you assign a completely new value to the list variable, there is no change in the behavior of the loop, because a reference to the old list was already captured at the outset (that is the significance of the listRef variable in my explanation of how this construct works). So:
set L to {1, 2, 3}
repeat with x in L
set L to {"Mannie", "Moe", "Jack"}
display dialog x -- 1, then 2, then 3
end repeat
Why does that work? It's because on each repetition, x takes on these values, just as before:
a reference to item 1 of {1, 2, 3}
a reference to item 2 of {1, 2, 3}
a reference to item 3 of {1, 2, 3}
You changed what L points to, but listRef already holds a reference to the original list. On the other hand, if you mutate the list in place, listRef is still a reference to that same list, so it acquires the mutations:
set L to {1, 2, 3}
repeat with x in L
display dialog x -- 1 (every time)
set beginning of L to contents of x
end repeat
L --{1, 1, 1, 1, 2, 3}
The dialog says "1" every time because, for example, by the time we come to the third loop and get a reference to item 3 of L, item 3 of L is 1. Observe that this code did not cause an infinite loop. That is the significance of step 2 in my explanation of how this construct works: theCount was evaluated once, before the first repetition, and you won't repeat more times than that.
A very odd thing happens when you combine repeat with...in directly with a class name as a way of gathering a list of elements, like this:
repeat with x in every class
To see what I mean, consider this code:
set total to 0
tell application "Finder"
count folders -- 6
repeat with x in every folder
set total to total