You are not logged in.
Pages: 1
A foreach loop for arrays, lists, maps, structs, grids, strings and number ranges.
This foreach was made using macros so you don't have to pass variables like arguments. You can access them inside of the loop directly.
Reserved keywords: foreach, foreach_rev, into, exec, as_list, as_grid, as_map, fe, fe_break, fe_continue, fe_return, global._FE_*
Changes
[v3.0.1] Renamed "invar" to "into"
[v3.0.0] A complete refractor
managed to speed it up by about 40%
no more warnings
DATA and VAR had to SWITCH PLACES
Syntax
foreach <data> into <var> exec
reversed loop:
foreach_rev <data> into <var> exec
<var> - an unused variable name to use
<data> - any supported data
Only data that dont use keys as indexes can be reversed
DS datatypes require a specification:
foreach <some_ds_map> as_map into v exec
foreach <some_ds_list> as_list into v exec
foreach <some_ds_grid> as_grid into v exec
The macro variable "fe" contains these variables of the current loop body:
fe.i - for array, list, grid, number range, string
fe.key - for map, struct
fe.xpos, fe.ypos - for grid
fe.set(val) - for anything but string and number range since those aren't references
To return from a function from within the loop use fe_return(val, [depth=1]);
To break the loop use fe_break;
To continue the loop use fe_continue;
While using fe_return you need to pay attention how "deep" the return is. If fe_return is called in a nested fe loop you have to specify how many fe loops to break using the depth parameter otherwise the stack doesn't get cleared correctly and it will cause unpredictable behaviour. (tbh I wouldn't return from within fe loops at all)
EXAMPLES HERE:
https://github.com/mh-cz/GameMaker-Foreach
/// Foreach 3.0.1
//
// foreach <data> into <var> exec
//
// <var> - variable
// <data> - any supported data
//
/// GMLscripts.com/license
#region GLOBAL
global._FE_STACK = array_create(10, undefined);
global._FE_CURR_STACK_INDEX = -1;
global._FE = undefined;
#endregion
#region MACROS
#macro fe global._FE
#macro fe_break fe.fn = fe.end_loop; break
#macro fe_continue break
#macro fe_return return _FE_RETURN
#macro as_list ,undefined,ds_type_list
#macro as_map ,undefined,ds_type_map
#macro as_grid ,undefined,ds_type_grid
#macro foreach \
for(_FE_AUTO_(
#macro foreach_rev \
for(_FE_AUTO_REV_(
#macro into \
); fe.fn(); ) for(var
#macro exec \
= fe.get(); true; break)
#endregion
#region RETURN
function _FE_RETURN(val, _depth = 1) {
repeat(_depth) fe.end_loop();
return val;
}
#endregion
#region AUTO
function _FE_AUTO_(a1, a2 = undefined, a3 = undefined) {
if a2 == undefined and a3 != undefined switch(a3) {
case ds_type_list: fe = new _FE_LIST_(a1); break;
case ds_type_map: fe = new _FE_MAP_(a1); break;
case ds_type_grid: fe = new _FE_GRID_(a1); break;
default: throw "Unsupported DS type";
}
else if is_array(a1) fe = new _FE_ARRAY_(a1);
else if is_string(a1) fe = new _FE_STRING_(a1);
else if is_struct(a1) fe = new _FE_STRUCT_(a1);
else if is_numeric(a1) {
if a2 == undefined fe = new _FE_RANGE_(0, a1, 1);
else if a3 == undefined fe = new _FE_RANGE_(a1, a2, 1)
else fe = new _FE_RANGE_(a1, a2, a3)
}
else throw "Unsupported type";
global._FE_STACK[@ ++global._FE_CURR_STACK_INDEX] = fe;
}
function _FE_AUTO_REV_(a1, a2 = undefined, a3 = undefined) {
if a2 == undefined and a3 != undefined switch(a3) {
case ds_type_list: fe = new _FE_LIST_REV_(a1); break;
case ds_type_map: fe = new _FE_MAP_(a1); break;
case ds_type_grid: fe = new _FE_GRID_REV_(a1); break;
default: throw "Unsupported DS type";
}
else if is_array(a1) fe = new _FE_ARRAY_REV_(a1);
else if is_string(a1) fe = new _FE_STRING_REV_(a1);
else if is_struct(a1) fe = new _FE_STRUCT_(a1);
else if is_numeric(a1) {
if a2 == undefined fe = new _FE_RANGE_(a1, 0, 1);
else if a3 == undefined fe = new _FE_RANGE_(a2, a1, 1)
else fe = new _FE_RANGE_(a2, a1, a3)
}
else throw "Unsupported type";
global._FE_STACK[@ ++global._FE_CURR_STACK_INDEX] = fe;
}
#endregion
#region ARRAY
function _FE_ARRAY_() constructor {
data = argument0;
len = array_length(data);
i = -1;
static set = function(v) {
data[@ i] = v;
}
static next = function() {
return ++i < len ? true : end_loop();
}
static get = function() {
return data[@ i];
}
static end_loop = function() {
global._FE = --global._FE_CURR_STACK_INDEX != -1 ? global._FE_STACK[@ global._FE_CURR_STACK_INDEX] : undefined;
return false;
}
fn = next;
}
function _FE_ARRAY_REV_() constructor {
data = argument0;
len = array_length(data);
i = len;
static set = function(v) {
data[@ i] = v;
}
static next = function() {
return --i > -1 ? true : end_loop();
}
static get = function() {
return data[@ i];
}
static end_loop = function() {
global._FE = --global._FE_CURR_STACK_INDEX != -1 ? global._FE_STACK[@ global._FE_CURR_STACK_INDEX] : undefined;
return false;
}
fn = next;
}
#endregion
#region STRING
function _FE_STRING_() constructor {
data = argument0;
len = string_length(data);
i = -1;
static set = function(v) {}
static next = function() {
return ++i < len ? true : end_loop();
}
static get = function() {
return string_char_at(data, i+1);
}
static end_loop = function() {
global._FE = --global._FE_CURR_STACK_INDEX != -1 ? global._FE_STACK[@ global._FE_CURR_STACK_INDEX] : undefined;
return false;
}
fn = next;
}
function _FE_STRING_REV_() constructor {
data = argument0;
len = string_length(data);
i = len;
static set = function(v) {}
static next = function() {
return --i > -1 ? true : end_loop();
}
static get = function() {
return string_char_at(data, i+1);
}
static end_loop = function() {
global._FE = --global._FE_CURR_STACK_INDEX != -1 ? global._FE_STACK[@ global._FE_CURR_STACK_INDEX] : undefined;
return false;
}
fn = next;
}
#endregion
#region LIST
function _FE_LIST_() constructor {
data = argument0;
len = ds_list_size(data);
i = -1;
static set = function(v) {
data[| i] = v;
}
static next = function() {
return ++i < len ? true : end_loop();
}
static get = function() {
return data[| i];
}
static end_loop = function() {
global._FE = --global._FE_CURR_STACK_INDEX != -1 ? global._FE_STACK[@ global._FE_CURR_STACK_INDEX] : undefined;
return false;
}
fn = next;
}
function _FE_LIST_REV_() constructor {
data = argument0;
len = ds_list_size(data);
i = len;
static set = function(v) {
data[| i] = v;
}
static next = function() {
return --i > -1 ? true : end_loop();
}
static get = function() {
return data[| i];
}
static end_loop = function() {
global._FE = --global._FE_CURR_STACK_INDEX != -1 ? global._FE_STACK[@ global._FE_CURR_STACK_INDEX] : undefined;
return false;
}
fn = next;
}
#endregion
#region STRUCT
function _FE_STRUCT_() constructor {
data = argument0;
key = "";
keys = variable_struct_get_names(data);
len = array_length(keys);
i = -1;
static set = function(v) {
data[$ i] = v;
}
static next = function() {
return ++i < len ? true : end_loop();
}
static get = function() {
key = keys[@ i];
return data[$ key];
}
static end_loop = function() {
global._FE = --global._FE_CURR_STACK_INDEX != -1 ? global._FE_STACK[@ global._FE_CURR_STACK_INDEX] : undefined;
return false;
}
fn = next;
}
#endregion
#region MAP
function _FE_MAP_() constructor {
data = argument0;
key = "";
keys = ds_map_keys_to_array(data);
len = array_length(keys);
i = -1;
static set = function(v) {
data[? key] = v;
}
static next = function() {
return ++i < len ? true : end_loop();
}
static get = function() {
key = keys[@ i];
return data[? key];
}
static end_loop = function() {
global._FE = --global._FE_CURR_STACK_INDEX != -1 ? global._FE_STACK[@ global._FE_CURR_STACK_INDEX] : undefined;
return false;
}
fn = next;
}
#endregion
#region GRID
function _FE_GRID_() constructor {
data = argument0;
xpos = 0;
ypos = 0;
w = ds_grid_width(data);
h = ds_grid_height(data);
len = w * h;
i = -1;
static set = function(v) {
data[# xpos, ypos] = v;
}
static next = function() {
return ++i < len ? true : end_loop();
}
static get = function() {
xpos = i mod w;
ypos = i div h;
return data[# xpos, ypos];
}
static end_loop = function() {
global._FE = --global._FE_CURR_STACK_INDEX != -1 ? global._FE_STACK[@ global._FE_CURR_STACK_INDEX] : undefined;
return false;
}
fn = next;
}
function _FE_GRID_REV_() constructor {
data = argument0;
xpos = 0;
ypos = 0;
w = ds_grid_width(data);
h = ds_grid_height(data);
len = w * h;
i = len;
static set = function(v) {
data[# xpos, ypos] = v;
}
static next = function() {
return --i > -1 ? true : end_loop();
}
static get = function() {
xpos = i mod w;
ypos = i div h;
return data[# xpos, ypos];
}
static end_loop = function() {
global._FE = --global._FE_CURR_STACK_INDEX != -1 ? global._FE_STACK[@ global._FE_CURR_STACK_INDEX] : undefined;
return false;
}
fn = next;
}
#endregion
#region RANGE
function _FE_RANGE_() constructor {
from = argument0;
to = argument1;
step = abs(argument2) * sign(to - from);
i = from - step;
static set = function(v) {}
static next = function() {
i += step;
return (from < to and i <= to) or (from > to and i >= to) ? true : end_loop();
}
static get = function() {
return i;
}
static end_loop = function() {
global._FE = --global._FE_CURR_STACK_INDEX != -1 ? global._FE_STACK[@ global._FE_CURR_STACK_INDEX] : undefined;
return false;
}
fn = next;
}
#endregion
Last edited by maras (2024-03-07 08:46:10)
I'm on the official GM discord > maras_cz
Offline
Whoa! I'll have to think about this.
Abusing forum power since 1986.
Offline
Main issue with this script is that it uses macros to make some tricks with GML, and replace things on compiling game. While this is OK for very advanced programmers, I think it's not a style that should be promoted for clean code.
Offline
Main issue with this script is that it uses macros to make some tricks with GML, and replace things on compiling game. While this is OK for very advanced programmers, I think it's not a style that should be promoted for clean code.
Well it was never really meant to be clean. The main goal was to make it look simple, quick to type and being able to access variables without passing them as arguments first. Dirty macros were the only thing that came to mind
Last edited by maras (2022-07-25 10:18:44)
I'm on the official GM discord > maras_cz
Offline
Pages: 1