Jump to content


Photo

foreach


  • Please log in to reply
2 replies to this topic

#1 IceMetalPunk

IceMetalPunk

    InfiniteIMPerfection

  • GMC Elder
  • 9603 posts
  • Version:GM:Studio

Posted 04 September 2011 - 01:51 AM

When you have a list of items--say, a ds_list (linked list for those with experience outside of GM) or an array--it's often useful to iterate over all items in the list and perform the same function with each entry. For example, adding every value in the list/array to a sum to be averaged (or not) later.

Currently, you can do that with a for() loop like so:

for (p = 0; p < SIZE_OF_LIST; p += 1) {
  value = LIST[p] OR ds_list_find_value(LIST, p);
  /* Code here */
}

That's quite a lot of typing for such a common process. Most other languages have some sort of foreach implementation, where the code above reduces to this:

/* In PHP */
foreach($LIST as $p => $value) {
  /* Code here */
}

/* In Javascript */
foreach (p in LIST) {
  /* Code here */
}

See how much more convenient that is? But, sadly, we can't do that in GML...or can we?

This script implements a foreach() functionality in GM:

foreach(LIST, CODE, COUNT[, ARGUMENTS]):
/* foreach(arrayList, action[, count[, arg1, arg2, arg3,...,arg13]])

Arguments:
arrayList - The string name of an array or the ID of a ds_list.
action - Either the ID of a script to execute or a string of GML to execute.
[count] - If arrayList is an array, this must be specified. It's the number
          of elements in the array. If arrayList is a ds_list, you can set
          this to anything, like 0, -1, whatever.
[arg1...arg13] (Optional) - Here you may specify up to 13 arguments to be 
                            passed to your script or GML code. These are
                            accessed normally in the script/code, using
                            argumentN or argument[N] syntax.
                            
Returns: True if ANY of the script calls return true, false otherwise.

Notes: This will iterate through your array or list and call your script or code
       once for each element. The argument0 of each call will always be the
       current value, and arguments 1 through 13 will be any arguments you've
       supplied to foreach() after the count.
       
       For example:
       
       arr[0]="Hi";
       arr[1]="Bye";
       arr[2]="Hello";
       arr[3]="Hola";
       
       foreach("arr", "show_message(argument0+' and '+argument1)", 4, "Common Argument");
       
       That will show 4 messages:
       
       Hi and Common Argument
       Bye and Common Argument
       Hello and Common Argument
       Hola and Common Argument
       
       The functionality is similar if you supply a ds_list, only the count
       parameter is ignored. And it's also similar if you supply a script ID
       instead of a string of GML--just remember the script ID is NOT a string!
       
       Note that array names may include global., local., or self. prefixes
       and will be processed correctly, as long as they're supplied as strings.
       
       Any code or scripts used here may return either true or false. They may
       also not return anything, in which case they're treated as a false return.

*/

var code, p, current, list, count, ret, is_ds, is_global, is_script;
list=argument0;
is_ds = is_real(list);
is_global = false;

if (!is_ds) {
  if (string_copy(list, 1, 7)=="global.") {
    is_global=true;
    list=string_delete(list, 1, 7);
  }
  else if (string_copy(list, 1, 6)=="local.") {
    is_global=false;
    list=string_delete(list, 1, 6);
  }
  else if (string_copy(list, 1, 5)=="self.") {
    is_global=false;
    list=string_delete(list, 1, 5);
  }
}

code=argument1;
is_script=is_real(code);

count=argument2;
ret=false;

if (is_ds) { count=ds_list_size(list); }

for (p=0; p<count; p+=1) {
  if (is_ds) { current=ds_list_find_value(list, p); }
  else if (is_global) { current=variable_global_array_get(list, p); }
  else { current=variable_local_array_get(list, p); }
  
  if (is_script) { ret=(ret || script_execute(code, current, argument3, argument4, argument5, argument6, argument7, argument8, argument9, argument10, argument11, argument12, argument13, argument14, argument15)); }
  else { ret=(ret || execute_string(code, current, argument3, argument4, argument5, argument6, argument7, argument8, argument9, argument10, argument11, argument12, argument13, argument14, argument15)); }
}

return ret;

The supplied list can be either a numerical ID of a ds_list or a string name of an array. For arrays, you may use global., local., and self. prefixes and they'll work just fine.

The code can be either a string of GML to execute or a numerical ID of a script to execute. If any iteration has your code return true, the foreach() call will return true; if all iterations return false, foreach() will return false. Any code or script that does not return a value is counted as false as per GM's own script handling.

-IMP
  • 0

:GM123: Are you an artist who likes creating original creature designs? Maybe you can help out with Elementa here! Give it a look; you might like the idea :)

:bunny: :excl: :bunny: :excl: :bunny: :excl: :bunny: :excl: :bunny: :excl: :bunny: :excl: :bunny: :excl: :bunny: :excl: :bunny: :excl: :bunny:


#2 Jeggy94

Jeggy94

    GMC Member

  • GMC Member
  • 95 posts
  • Version:GM:Studio

Posted 15 August 2013 - 12:27 AM

Could you rewrite this script in some way so it's supported in GM:S also? as variable_global_array_get is obsolete 


  • 0
android-app-on-google-play-01-logo.png

#3 Indecom4000

Indecom4000

    GMC Member

  • GMC Member
  • 1636 posts
  • Version:GM:Studio

Posted 23 August 2013 - 05:14 PM

as well as execute_string(). neither of those work in studio. This sounds useful otherwise.


  • 0

LD-Logo-sml.png