Home > Net >  How to use list values inside lists in Netlogo
How to use list values inside lists in Netlogo

Time:04-08

When I setup my model, I also upload a csv file containing multiples columns, after some research, I found that it is read as a list of lists (each line being a list).

set yield-data sentence yield-data (list (list file-read file-read file-read file-read file-read))

When I run the model (land use model), for each patch, a value from the list of lists (yield-data from above) must be used in a simple arithmetic that will define its next land use category. I'm not there yet, I first aim to figure out how to look-up for values inside the my-list

See below the reproducible code, with my-list instead of yield-data, except for the procedure to test for which I struggling. I'm still new to object-oriented modeling...

globals [
  my-list
  my-rd-list
]

patches-own [
  year
  varA
  varB
  temp-varC
]

to setup
  ;; here I just create patches with different values that also appear in the list
  ca
  resize-world 9 * 0 ( 9 * 1 )  ( 9 * -1 ) 9 * 0 
  set my-list [[2015 3 2 8] [2016 5 1 10] [2017 7 0 12]] 
  ask patches with [pxcor < 3] [set year 2015] 
  ask patches with [pxcor > 3] [ set year 2016]
  ask patches with [pxcor > 6] [ set year 2017]
  ask patches with [pycor > -3] [ set varA 3] 
  ask patches with [pycor < -3] [ set varA 5]
  ask patches with [pycor < -6] [set varA 7]
  ask patches [set varB random 3]
end

to test
;; need to add a "while n <= number of list inside the list"
let n 1
  foreach item n my-list [
    x ->
   ask patches with [year = item 0 item n my-list and varA = item 1 item n my-list and varB = item 2 item n my-list] [set pcolor orange set temp-varC item 3 item n my-list] 
    set n n   1
  ]
end

The idea is that for each list (for example [2015 3 2 8], the last value of the list is used to do the little math afterwards for the patch that patches the criteria of having year = 2015, varA= 3 and varB=2, I though it would be easier to store it in the patch as a temporary variable. The code is now working thanks to understanding better how list work. However,

1) I'm however missing how to count how many list there are in the main list - or how many lines there are in the original txt file I mentionned in the beginning.

2) I'd like varA to be inside, let's say, -1 / 1 around the value of item 1 item n my-list, something like here-below that does not work

ask patches with [year = item 0 item n my-list and varA < item 1 1 (item n my-list) and varA > item 1 - 1 (item n my-list) and varB = item 2 item n my-list] [set pcolor orange set temp-varC item 3 item n my-list]

Then I have a "fundamental" question : when one patch will be checked, his temp-varC updated and varA updated too based on the values in my-list : can it still be elligble in that loop for a next update of temp-varC ? Because varA will change so the combination of "items" will change and it can have an updated temp-varC (but I don't want that!).

CodePudding user response:

On how to count how many lists are in my-list

Each inner list is just an item of my-lists, so length my-lists will tell you how many inner lists are there.

On the interval around varA

This only requires you to devise an arithmetical condition that mirrors what you want. I guess it can be done in at least a couple of ways.

For example, for the condition in which you want to apply this interval, instead of having something like:

varA = item 1 some-list

you could have have:

abs (varA - item 1 some-list) <= 1

Approach using while

Let me first give you a solution using while, because it is the way you approached the issue and because it is useful to clarify how to treat lists in this context. After that, a more concise approach using filter.

With the while approach you don't need foreach, because foreach runs a command for every item of a list. In your example, where you use foreach, you are telling your anonymous procedure to store as x each item of my-list (i.e. each inner list) one at a time, and to run some commands for each x. However, you never address x in such commands, which proves that foreach is superflous.

This means that, using the two pieces of information above about the length of a list and the interval in the condition, you can achieve your goal by just doing:

to test
  ask patches [
    let i 0
    while [i < length my-list] [
      let current-inner-list item i my-list
      ifelse ((year = item 0 current-inner-list) AND (abs (varA - item 1 current-inner-list) <= 1) AND (varB = item 2 current-inner-list))
        [set temp-varC item 3 current-inner-list
         stop]
        [set i i   1]
    ]
  ]
end

This way, the loop will check inner lists until it finds the correct one, then sets temp-varC and then executes stop so that the loop ends without having to iterate through all of my-list.

Note that parentheses around individual booleans are optional, I use them for readability.

Approach using filter

A more concise approach uses filter: this primitive takes a reporter and a list as arguments, and returns (reports, in NetLogo jargoon) a list containing only the items from the original list for which the reporter evaluates as TRUE.

In your case, this means that you can do:

to test
  ask patches [
    set temp-varC last (first (filter [current-inner-list -> (item 0 current-inner-list = year) AND (abs (varA - item 1 current-inner-list) <= 1) AND (item 2 current-inner-list = varB)] my-list))
  ]
end

Let's break this code down.

The filter command is being passed an anonymous reporter (the usual concatenation of conditions) and my-list, so it will report only the items of my-list for which all of those conditions evaluate as TRUE.

For example: for a patch that has year = 2017, varA = 7 and varB = 0, the filter [current-inner-list -> (item 0 current-inner-list = year) AND (abs (varA - item 1 current-inner-list) <= 1) AND (item 2 current-inner-list = varB)] my-list statement will report [[2017 7 0 12]], which is my-list but containing only that one item.

Given that you are only interested in extracting 12, you need two more steps:

  1. Extracting the only inner list by using first or last or item 0. Therefore first (filter [current-inner-list -> (item 0 current-inner-list = year) AND (abs (varA - item 1 current-inner-list) <= 1) AND (item 2 current-inner-list = varB)] my-list) reports [2017 7 0 12];
  2. Extracting the last item of this inner list by using last or item 3. Therefore last (first (filter [current-inner-list -> (item 0 current-inner-list = year) AND (abs (varA - item 1 current-inner-list) <= 1) AND (item 2 current-inner-list = varB)] my-list)) reports 12.

This is why doing set temp-varC <all of the above> does the job.

A note: this code with filter does not currently work with your example, because my-list does not currently contain enough combinations of values to cover all patches. This means that, for some patches, filter [current-inner-list -> (item 0 current-inner-list = year) AND (abs (varA - item 1 current-inner-list) <= 1) AND (item 2 current-inner-list = varB)] my-list will report an empty list [], and therefore running first (filter [current-inner-list -> (item 0 current-inner-list = year) AND (abs (varA - item 1 current-inner-list) <= 1) AND (item 2 current-inner-list = varB)] my-list) will give you an error. If, in the real model, my-list will contain any possible combination of year, varA and varB that a patch can have, then there is no problem. Otherwise, you would probably need to anticipate that possibility and use ifelse, or ifelse-value, to assign a default value to temp-varC in case no matching list is found inside my-list. Something like:

to test
  ask patches [
    let my-list-filtered filter [current-inner-list -> (item 0 current-inner-list = year) AND (abs (varA - item 1 current-inner-list) <= 1) AND (item 2 current-inner-list = varB)] my-list
    set temp-varC ifelse-value (empty? my-list-filtered) [0.5] [last first my-list-filtered]
  ]
end

Regarding multiple updates of temp-varC

No: the way ask works in NetLogo is that it first gathers all agents belonging to the agentset being summoned, and then each of them is asked to perform the actions in the command block. That's it, no dynamic update of agentsets.

This code verifies it:

to test-ask-command
  clear-all
  
  create-turtles 2 [
    set color 25
  ]
  
  ask turtles with [color = 25] [
    show "Hi!"
    ask other turtles [
      set color 35
    ]
  ]
end

This shows the following in the Command Center:

observer> test-ask-command
(turtle 1): "Hi!"
(turtle 0): "Hi!"

which means that the second turtle said hi even if, by the time it got to perform its action, its color had already been changed to 35.

Side notes

  1. Is it intentional that you are leaving some patches with year = 0 and with varA = 0? Because, in your setup, you are only using strictly-greater and strictly-less symbols - so that all patches with pxcor = 3 don't get to set year and all patches with pyxcor = -6 don't get to set varA.
  2. To be precise with style, I suggest choosing variable names that are significant without the need of capital letters: if you inspect any patch, you will see that for example varA is just vara for NetLogo.
  • Related