DekGenius.com
[ Team LiB ] Previous Section Next Section

13.12 List

A list is a collection, corresponding roughly to what many other languages would call an array—it's an ordered set of values. These values are its items. Each value can be of any datatype (including a list).

A literal list is delimited by curly braces. Its contents can be literal values, variable names, or any other expressions that AppleScript can evaluate meaningfully; they are separated by commas. The literal empty list is just a pair of curly braces. So:

set empty to {}
set pep to {"Mannie", "Moe"}
set pep3 to "Jack"
set pep to pep & {pep3} -- {"Mannie", "Moe", "Jack"}

You can assign a list of values to a literal list of variable names or other references as a shorthand for performing multiple assignments. The assignments are performed pairwise in order: item 1 to item 1, item 2 to item 2, and so on. If the list of values is too long, the extra values are ignored; if it's too short, there's a runtime error. (See Section 7.1 and Section 10.2.2.) For example:

tell application "Finder"
        set {oldname1, oldname2} to {name of folder 1, name of folder 2}
        set {name of folder 1, name of folder 2} to {"f1", "f2"}
end tell

When you use set (as opposed to copy) to set a variable to a value that is a list, you set the variable by reference. This means that the list is not copied; the variable's name becomes a new name for the list, in addition to any names for the list that may already exist. The same is true when a list is passed as a parameter to a handler. This special treatment is in common between lists, records, dates, and script objects. (See Section 8.4 and Section 9.5.1.) For example:

set L1 to {"Mannie", "Moe"}
set L2 to L1
set end of L1 to "Jack"
item 3 of L2 -- "Jack"

Unlike a string, a list can be modified in place. You can replace individual items, and you can add a new item to the beginning or end of a list. This is often a reason for using a list, instead of a string, for an extended series of operations, and then coercing to a string afterwards.

A list is stored internally as a data structure called a vector. This means that all the items of the list are accessible with equal efficiency; if a list has 100 items, it doesn't matter whether you refer to item 1 or item 100—AppleScript can access the item instantly. Under this vector implementation, setting an existing item of a list to a new value is efficient, because all that happens is that the new value is copied to the location in memory where the old value used to be.

set L to {"Mannie", "Moe"}
set item 1 of L to "Larry"
L -- {"Larry", "Moe"}

Also, setting the beginning or end of a list (as a way of appending to the list) is efficient. The reason for this is that nothing moves in memory except the new value, and the list is told it is one item longer than it was. So:

set L to {"Moe"}
set end of L to "Jack"
set beginning of L to "Mannie"
L -- {"Mannie", "Moe", "Jack"}

On the other hand, the vector implementation means that there is no efficient way to insert an item into the middle of an existing list, or to delete an item of a list, and there are no built-in commands for these operations. You'll probably want to arm yourself with a small arsenal of list utility handlers. For an item-deletion handler, see Section 5.5. See Section 9.5.3 for a filter handler, and Section 11.6 for a find-in-list handler. Here's an item-insertion handler:

on listInsert(L, what, ix)
        if ix = 1 then
                return {what} & L
        else
                return {item 1 of L} & listInsert(rest of L, what, ix - 1)
        end if
end listInsert
listInsert({"Mannie", "Jack"}, "Moe", 2) -- {"Mannie", "Moe", "Jack"}

A surprising feature of AppleScript lists is that they can recurse. This feature is actually a natural consequence of the foregoing. We know that when you use set to assign a list as a value, you set by reference; you create a pointer to the existing list. We know that a list item can be a list. So if a list item is created using set, it's a pointer to a list. Well then, it can be a pointer to the same list. For example:

set L to {"Mannie", "Moe", "Jack"}
set end of L to L
item 4 of item 4 of item 4 of item 4 of item 4 of item 4 of L
-- {"Mannie", "Moe", "Jack", {"Mannie", "Moe", "Jack", ...}}

Where did all those items of items of items come from? And what are those ellipses at the end of the result? We've formed a recursive list. The fourth item of L is a pointer to L itself. So if we look at the fourth item of L, we dereference this pointer and presto, we're looking at L. The fourth item of that is a pointer to L itself—and so forth. In other words, this is not an infinite list in the sense that it genuinely goes infinitely deep or far in memory; if it were, flames would come out of your computer. It's just a data structure of four items that goes round and round in a tight little circle.

Multiple lists can mutually recurse:

set L1 to {"Mannie", "Moe", "Jack"}
set L2 to {"Curly", "Larry"}
set end of L1 to L2
set end of L2 to L1
L2 -- "Curly", "Larry", {"Mannie", "Moe", "Jack", 
                {"Curly", "Larry", {"Mannie", "Moe", "Jack", ...}}}}

I don't know of any practical use for this curious feature, and I wouldn't count on its being supported in future versions.

AppleScript contains a couple of built-in classes that are really just lists by another name, along with some coercion rules. For example, bounding rectangle is a list of four integers, rgb color is a list of three integers, and point is a list of two integers. There may be others like this that I haven't stumbled upon.

13.12.1 List Properties

The following are the properties of a list. They are all read-only.


length

The number of items in the list. You can get the same information by sending the list the count message.


rest

Everything in the list except its first item.


reverse

The list in reverse order.

13.12.2 List Elements

The following are the elements of a list:


item

An item of the list, specified by index number, by range, or with every.


classname

An item whose class is classname, specified by index number, by range, or with every. This is the closest thing a list has to a boolean test element specifier. For example, you can't say:

item 2 of {23, "skiddoo", "catch", 22} whose class is integer -- error

but you can say:

integer 2 of {23, "skiddoo", "catch", 22} -- 22

However, this does not work if the class you're asking about is class. This is a known bug.

13.12.3 Speed of List Access

When you access an element or property of a list, it is much faster to target a reference to the list, or the list as a script property, than to target the list directly. (Except when you set the beginning of and end of a list, that is. These operations are fast in any case because they have a special efficiency shortcut built in, as mentioned already.) Here's an adaptation of Apple's own example illustrating this point. Let's start with a version where we don't use a reference:

set L to {}
set total to 0
set bignum to 5000
repeat with i from 1 to bignum
        set end of L to i
end repeat
repeat with i from 1 to bignum
        set total to total + (item i of L)
end repeat
total -- 12502500, and it takes about 22 seconds to run on my machine

Now here's a version where we use a reference:

set L to {}
set refL to a reference to L
set total to 0
set bignum to 5000
repeat with i from 1 to bignum
        set end of L to i
end repeat
repeat with i from 1 to bignum
        set total to total + (item i of refL)
end repeat
total -- 12502500, and it took less than a second

That is an extraordinary speed difference. Now here's the really strange part: you don't actually have to form a value that's a reference; you can get the same speed bump by referring to the list as a script property:

set L to {}
set total to 0
set bignum to 5000
repeat with i from 1 to bignum
        set end of L to i
end repeat
repeat with i from 1 to bignum
        set total to total + (item i of my L)
end repeat
total -- 12502500, and it took less than a second

The key word in that code is my. Take it away, and the code takes 22 seconds to run; put it back, and the code runs in less than a second.

Now suppose all of that code is part of a handler, where L is a local variable. You can't take a reference to L, so you'd have to use the trick of making L a script property. Again, there is then no need to say a reference to explicitly; merely referring to the script property is sufficient:

on myHandler(  )
        set L to {}
        script myScript
                property refL : L
        end script
        set total to 0
        set bignum to 5000
        repeat with i from 1 to bignum
                set end of L to i
        end repeat
        repeat with i from 1 to bignum
                set total to total + (item i of myScript's refL)
        end repeat
        return total
end myHandler
myHandler(  ) -- 12502500, and it took less than a second

I have no clear explanation for this behavior. It appears that when you access a property of a list by simply using the name of a variable whose value is that list, AppleScript substitutes a copy of the literal value of the list; all that copying takes time when the list is large and there are many repetitions. Whatever the cause, complaints by users that AppleScript is slow are often attributable to a failure to use these techniques .

    [ Team LiB ] Previous Section Next Section