Undertanding map and filter.

Freeform discussion about anything related to modding Transcendence.
Post Reply
Militia Captain
Militia Captain
Posts: 533
Joined: Tue Nov 05, 2013 9:56 am

Wed Feb 08, 2017 2:47 am

Hope this helps because how to use these functions eluded me for many months.

From the xelerus functionlist:

map (map list variable expression)
filter (filter list variable expression)

These functions both work on lists. And the result will also be a list, the original list with something done to it.
In both cases immediately after the function name comes 'list'. So the first lot of brackets or the first word inside this function will be code that makes the list or the name of the list.

The next part of the function is the 'variable'. This is one list entry. What the functions do is take one list entry at a time and perform the 'expression' code on it. The function then takes the next list entry and does the same 'expression' code on it. It repeats this, working its way through the list until it runs out of list entries. Note that this 'variable' must always be in the 'map' or 'filter' code at least twice. Once just after the list/list code and at least once in the 'expression' code.

As an example in SOTP;

Code: Select all

(setq listOfSystemNames (map (sysGetNodes) variable (sysGetName variable)))
will give you a list of the ~25 system names called 'listOfSystemNames'.

It does this using 'sysGetNodes' to generate a list of all the nodeIDs in SOTP. These nodeIDs are usually two or three digits long and are used by the game to identify star systems instead of using system names. (Most systems in SOTP have randomly generated names. eg the first system after Eridani always has the nodeID 'C1' but can be named either Groombridge, Lalande. 5 Indi or Foum Alhaut.)

So using 'sysGetNodes' will make the 'list' part of 'map' ~25 nodeIDs. Something like this.
(SE "C1" "C3" "C3A" BA "C4" CP SK "C5A" "C5" "C6" "C6A" CD "C7" "C8" "C9" "A1" EC "A3" "A3A" PJ "A5" "A7" "G1" "G2" Elysium Huaramarca)
Note that even though Elysium and Huaramarca come up like this they are not system names. They are still nodeIDs. More on this later.

What 'map' does is take the first nodeID, in this case "SE", and call it 'variable'. It then runs the next code, 'sysGetName', on 'variable' and gets "Eridani" which is the system name of nodeID "SE". Note that the 'variable' doesn't have to be called that. It can be called anything as long as the same name is used inside the 'expression' code. Calling it 'listEntry' or 'node' would work and would be a bit more descriptive.
It then starts a new list with "Eridani" as the first entry.
Next it takes the next nodeID, in this case "C1", calls it 'variable', runs the 'expression' code on it and gets the result, for the game I had running, "Lalande". It adds this to the new list and then goes on to do the same thing to the third list entry "C3". It will keep doing this until it runs out of list entries.

So the end result would look like this,

(Eridani Lalande Orgos Lacaille "Rigel Aurelius" "Hena's Star" Charon "St. Katharine's Star" "Mu Casser" Humboldt Acheron Junger Sanctuary Domitus Exael "Jiang's Star" "Severian's Star" "Eta Ceti" Qilin "St. Esperance's Star" "Point Juno" Vori Dantalion "HD 11901" Heretic Elysium Huaramarca)
which are the star system names in this particular game of SOTP.

Note here that because the nodeIDs 'Elysium' and 'Huaramarca' have had the 'sysGetName' code run on them they are now system names and no longer nodeIDs.

As a further example we will filter the list we just made, 'listOfSystemNames'.

This code:

Code: Select all

(filter listOfSystemNames name (eq name "Elysium"))
will give us a list of one system name. (Elysium).

The 'filter' function
(filter list variable expression)
filters a list, in this case 'listOfSystemNames', and will only add a list entry to the new list if 'name' matches the 'expression' part of the code (returns True in code jargon).

So 'filter' takes the first list entry of 'listOfSystemNames', in this case 'Eridani', and calls it 'name' (instead of 'variable' this time). It then runs the 'expression' code on 'name' and if it is True, it adds it to the new list. In this case 'Eridani' is not 'eq'ual to 'Elysium' so the new list still has no entries. It then takes the second list entry from 'listOfSystemNames', here 'Lalande', calls it 'name' and checks to see if it is equal to 'Elysium'. Once again it is not, so no entry is added to the new list.
'filter' then continues through the list until the end. Only when it gets to the second to last entry 'Elysium' does the 'expression' code match. The code '(eq name "Elysium")' is True when 'name' is 'Elysium'. So the final list consists of one entry, (Elysium).

The 'filter' explanation on xelerus had this:
Filter takes the passed in list and returns a new list made up of elements of the passed in list that returned true for the boolean function.
Which is quite correct but was gibberish to me until I worked out what's going on in there.:lol:

If someone understands what the options ['excludeNil|'original|'reduceMax|'reduceMin] do, could they post that info here so the OP can be updated.Thanks.
Functionlist 1.7b4
(map list ['excludeNil|'original|'reduceMax|'reduceMin] var exp) -> list
Last edited by relanat on Sun Feb 12, 2017 3:09 am, edited 1 time in total.

Commonwealth Pilot
Commonwealth Pilot
Posts: 93
Joined: Thu Apr 07, 2011 9:05 pm

Wed Feb 08, 2017 4:48 pm

Code: Select all

(map list ['excludeNil|'original|'reduceMax|'reduceMin] var exp) -> list
  • excludeNil - exclude items where expression evaluates to Nil
  • The four reduce options (reduceMin, reduceMax, reduceSum, reduceAverage) will reduce the output to a single value rather than a list. e.g. reduceMin returns the minimum of the evaluated expressions
  • original will return the original elements of list rather than the evaluation of exp. This is typically used in combination with the other options

Code: Select all

(setq mylist '(-2 -1 3 5))

; Find minimum:
(map mylist 'reduceMin x x) -> -2
; Could also use:
(apply min mylist)

; Find number closest to zero
(map mylist '('original 'reduceMin) x (abs x)) -> -1

; Filter using map
(map mylist '('original 'excludeNil) x (geq x 0)) -> (3 5)

; Calculate sum of squares:
(map mylist 'reduceSum x (* x x)) -> 39

; Shallow copy
(map mylist 'original x x) -> (-2 1 3 5)

Militia Commander
Militia Commander
Posts: 439
Joined: Tue Mar 05, 2013 8:26 am

Thu Feb 09, 2017 4:34 am

This should probably be in Modding Reference, but good explanation.

Two very similar functions are enum and enumWhile. (enum list itemVar exp) is like map, except that it only returns the result of the last time the expression is evaluated, rather than a list of all the results. (enumwhile list condition itemVar exp) is like enum except that it also has a condition. After the variable is set to each item in the list, the condition is checked. If it's not nil, the expression is evaluated. If it is nil, enumWhile returns the result of the last time it was evaluated, and doesn't evaluate for the remaining items on the list.

It's also good to understand that the variables used for iteration in these functions - as well as (for) - have a scope of just the function. In other words, they won't affect or be affected by any variable with the same name outside the function. For instance, you could do: (enum listOfObjectsToDestroy gPlayerShip (objDestroy gPlayerShip)) and it would not destroy your ship unless it happened to be one of the items on listOfObjectsToDestroy, nor would it change the value of the global variable gPlayerShip. Thus, you don't need to declare these variables in a block, and shouldn't to avoid confusion.

Post Reply