Avoiding Multidimensional Lists in Function Results

AppleScript’s handling of multidimensional lists often proceeds at a glacial pace, so I prefer to avoid lists of lists whenever possible. This presents a special problem when a function needs to return two or more lists to its caller.

When a function must return two or more values, it’s a simple matter to place the values in a list, and return that.
Similar can done with lists, by creating a one dimensional list holding the count of the first list, the values of the first list, the count of the second list, the values of the second list, etc. etc.

Here’s a little code I use for functions that must return two or three lists. The general case, returning n lists, likely wouldn’t be too hard to write, but so far variants on these two tidbits have met my needs.

To see what they do, open up the Editors “Result” pane before running them.

-- Avoid multidimensional lists in function returns
-- by packing and unpacking a 1D list
-- 2 lists
-- BP 2011

set pkg to pack2Lists()

set ra1count to item 1 of pkg
set ra2count to item (ra1count + 2) of pkg -- (const is 1 + previous number of items)
set ra1 to items 2 thru (ra1count + 1) of pkg
set ra2 to items (ra1count + 3) thru (ra1count + ra2count + 2) of pkg
return ra1 --ra2 --pkg  -- change the returned variable to see what's in each List.
---------------

on pack2Lists()
	set ra1 to {1, 2, 3, 4, 5, 6}
	set ra2 to {"kk", "rest", "muck", "twenty"}
	set rez to (count of ra1) & ra1 & (count of ra2) & ra2
	return rez
end pack2Lists


-- Avoid multidimensional lists in function returns
-- by packing and unpacking a 1D list
-- 3 Lists
-- BP 2011

set pkg to pack3Lists()

set ra1count to item 1 of pkg
set ra2count to item (ra1count + 2) of pkg
set ra3count to item (ra1count + ra2count + 3) of pkg -- (const is 1 + previous number of items)

set ra1 to items 2 thru (ra1count + 1) of pkg
set ra2 to items (ra1count + 3) thru (ra1count + ra2count + 2) of pkg
set ra3 to items (ra1count + ra2count + 4) thru (ra1count + ra2count + ra3count + 3) of pkg
return pkg --ra1 -- ra2 --ra3  -- change the returned variable to see what's in each List.
---------------

on pack3Lists()
	set ra1 to {1, 2, 3, 4, 5, 6}
	set ra2 to {"kk", "rest", "muck", "twenty"}
	set ra3 to {7, 9, 4, 3, 22}
	set rez to (count of ra1) & ra1 & (count of ra2) & ra2 & (count of ra3) & ra3
	
	return rez
end pack3Lists

Well to be honest with you I don’t see any kind of problems with multidimensional lists. For our mind a one dimensional list seems easier but you create a lot of overhead here because listed lists have better performance than one huge list.

The return value of a handler that needs to return multiple values can be solved different ways. Give references as a parameter and create a void function or return a listed list and set it directly to a list of variables. both ways in one example where dummy1 is returning a listed list but is catched by setting it’s return value to a list of variables. Dummy2 get’s already declared variables and change their content so the we don’t have worry about return values.

--set values of listed list returned by dummy to list of variables
set {list1, list2} to dummy1()

--give a referene to the handler instead of a value
--first we need to create variables, doesn't really matter what value they get
set list3 to missing value
set list4 to missing value
--now these variables are filled by the handler
dummy2(a reference to list3, a reference to list4)

return {list1, list2, list3, list4}

on dummy1()
	set ra1 to {1, 2, 3, 4, 5, 6}
	set ra2 to {"value 1", "value 2", "value 3", "value 4"}
	return {ra1, ra2}
end dummy1

on dummy2(ra1, ra2)
	set contents of ra1 to {8, 9, 0, 11, 2, 3, 44}
	set contents of ra2 to {"text 1", "text 2", "text 3", "text 4"}
	return
end dummy2

Dummy2 handler seems very uncommon but people who already worked with other programming languages like C or it’s supersets know pointers and addresses and dummy2 can be compared with passing an address to a function/method.

I like playing with code too but in this case in any way the code is inferior to the standards.

That’s the relevant point here. What’s actually passed to a handler or returned from it is not the AppleScript value but a pointer to where the value’s stored in the computer’s memory. [See footnote.] The type, size, and content of an item therefore make no difference at all to the speed at which it’s returned from a handler. (NB. “Handler” in AppleScript, not “function”.)

Variables, list items, and record properties are similarly just pointers to the values they notionally “contain”. They don’t actually contain the values. A list containing two lists is simply a list containing two pointers, which is a whole lot faster to set up and use than all the counting and serial list generation suggested above:

set ra1 to {1, 2, 3, 4, 5, 6}
set ra2 to {"kk", "rest", "muck", "twenty"}

set rez to {ra1, ra2} -- Create a two-item list.

-- . as apposed to .

set rez to (count of ra1) & ra1 & (count of ra2) & ra2 -- Count the list ra1, implicitly coerce the result to list, and concatenate ra1 to it to create a third, (length of ra1 + 1)-item list. Then count the list ra2, implicitly coerce the result to a fourth list, and concatenate that to the third list to create a fifth, (length of ra1 + 2)-item list. Finally, concatenate ra2 to the result to create a sixth, (length of ra1 + length of ra2 + 2)-item list.

Footnote: I’m not absolutely sure how it works in Mac OS X, but on earlier systems, each pointer would lead in the first instance to an address where the system’s Memory Manager had stored the AppleScript value’s start address in memory. I imagine it’s a similar arrangement in Mac OS X.