How do you label output variables in an IDL FOR loop for further processing outside the loop in the same program?

1.7k Views Asked by At

I have a FOR loop like this:

FOR k = 1,216 DO  atom = G[*,0:*:(215+k)]  END

What I would like to be able to do is to store in memory the array for each atom, say, atom_k and then call these different variables to perform further operations outside the FOR loop. Conceptually, I want to label the "atom" variable with the "k" counter somewhat like this:

FOR k = 1,216 DO  atom(k) = G[*,0:*:(215+k)]  END

Of course, this doesn't work because "k" is no longer a label in this case. Does anyone know?

3

There are 3 best solutions below

0
On

I assume you're talking of IDL, the language developped by ITTVis. I don't understand the way you index G nor the END instruction, but I'm not working with latest version.

Try the EXECUTE command. This allows you execute statements at runtime.

FOR k = 1,216 DO status = EXECUTE('atom_'+strtrim(k,2)+' = G[,0::(215+'+strtrim(k,2)+')]') END

STRTRIM converts integers to string without blanks around.

0
On

Looks like you're trying to create a ragged array. What you want is a data structure like a list of lists, where the sublists have a different number of elements. This is version-dependent, but it looks like the list() function introduced in 8 would do exactly this.

atoms = list()
FOR k = 1,216 DO  atoms.add, G[*,0:*:(215+k)]  END

I'm actually not positive that list() is the proper way to create an empty list. Can't debug it because I'm not running 8.

0
On

There are a few options I'm aware of:

Of the following, #1 is the closest to what you requested, but I believe that #3 offers the best blend of usability for your mentioned goal, speed, and compatibility with different versions of IDL.

1) Using scope_varfetch with the /enter keyword:

pro foo, G
    for k = 1, 216 do begin
        varname = 'atom' + strtrim(k, 1)
        (scope_varfetch(varname, level=-1, /enter)) = G[*, 0:*:(215 + k)]
    endfor
end

This creates variables named atom1 through atom216 in the calling routine, or in $MAIN$ if executed interactively. I think this is the closest to what you requested. You can also access those variables directly from the calling function by name using syntax either like print, atom5 or like:

for k = 1, 216 do begin
    varname = 'atom' + strtrim(k, 1)
    print, scope_varfetch(varname)
endfor

2) Using an IDL 8.0+ List object:

atom = list(length=216)
for k = 0, 215 do atom[k] = G[*, 0:*:(216 + k)]

Note that the list is 0-indexed, meaning that the first element is zero, instead of one. Use atom[0] to access the first atom, and so on. To access the first index of the first atom, surround with parentheses and use an additional set of brackets to index: (atom[0])[0, 0]. The parentheses are necessary due to IDL's unusual order of operations.

3) Using a pointer array:

atom = ptrarr(216)
for k = 0, 215 do atom[k] = ptr_new(G[*, 0:*:(216 + k)])

Or with slightly different syntax:

atom = ptrarr(216, /allocate_heap)
for k = 0, 215 do *atom[k] = ptr_new(G[*, 0:*:(216 + k)])

This is the most efficient and highly compatible way to do it. Pointers and the associated ptrarr and ptr_new functions have been around since IDL 5.0, and they are much more efficient than lists, hashes, or scope_varfetch. Note that like the list, this is 0-indexed. Also, to access these values you have to "deference" them with a *, as in print, *atom[0] to print the first atom array, or print, (*atom[0])[0, 0] to print the first element of the first array and so on. The parentheses are required for the same reason as with the list. Setting values is also possible with syntax like (*atom[0])[1, 15] = new_values.

4) Using an IDL 8.0+ hash:

atoms = hash()
for k = 1, 216 do begin
    name = 'atom' + strtrim(k, 1)
    atoms[name] = G[*, 0:*:(215 + k)]
endfor

These can be referenced like atoms['atom0']. I don't think this is probably the best option in this case, since an array is more efficient, but it is very useful if there are additional string names you need to use, or if the data indexes are sparse.

5) Building a structure with create_struct:

atoms = !null
for k = 1, 216 do begin
    tagname = 'atom' + strtrim(k, 1)
    atoms = create_struct(atoms, tagname, G[*, 0:*:(215 + k)])
endfor

This is very slow, but relatively easy to understand once it's done. Get each array like atoms.atom1 and get an element like atoms.atom1[0, 0].