Search code examples
variablesdynamicnetlogo

Dynamic variable definition in NetLogo


Has anyone tried creating a dynamic list of variables. Variable names will be supplied via a csv file which will be loaded as part of setup. I'm having trouble with defining the variable names.

Working:

    create-turtles 1 [

      let attribute-name item 0 attributes

          set var attribute-name

         let ID "123". ; atribute-name is ID and it correctly defined by assigning value 123 to ID

      let attribute-value item 0 data

      run (word "set " attribute-name " " attribute-value)  ;

]

Not working:

create-turtles 1 [

      let attribute-name item 0 attributes

          set var attribute-name

          run (word "let " var " " 123)  --> This resolved to "let ID 123 ". However it didn't do anything

      let attribute-value item 0 data

      run (word "set " attribute-name " " attribute-value)  ; --> At this point still getting error that nothing named ID is defined.

Everything works if i define the variable name (ID) explicitly either as turtle variable or local variable.


Solution

  • NetLogo doesn't have a way of declaring variables dynamically at runtime, which is what it seems like you're trying to do. To just get things working, you'll need to declare your variables ahead of time, and it seems like you want to use turtles-own variables. Your example isn't complete, so I filled in some details here:

    turtles-own [ID Name Score]
    
    to setup
      clear-all
      let attributes ["ID" "Name" "Score"]
      let data [123 "Jeffrey" 100]
    
      create-turtles 1 [
        let attribute-name item 0 attributes
        let attribute-value item 0 data
        ; the `run` of the `set` string now works, but we need to quote strings
        let value-string ifelse-value is-string? attribute-value [(word "\"" attribute-value "\"")] [attribute-value]
        let set-command (word "set " attribute-name " " value-string)
        run set-command
      ]
    
      show [(list ID Name Score)] of turtles
      ; outputs `observer: [[123 0 0]]`
    end
    

    And we can generalize this just a bit with the range primitive for all attributes and data:

    turtles-own [ID Name Score]
    
    to setup
      clear-all
      let attributes ["ID" "Name" "Score"]
      let data [123 "Jeffrey" 100]
    
      create-turtles 1 [
        foreach range (length attributes) [ i ->
          let attribute-name item i attributes
          let attribute-value item i data
          ; the `run` of the `set` string now works, but we need to quote strings
          let value-string ifelse-value is-string? attribute-value [(word "\"" attribute-value "\"")] [attribute-value]
          let set-command (word "set " attribute-name " " value-string)
          run set-command
        ]
      ]
    
      show [(list ID Name Score)] of turtles
      ; outputs `observer: [[123 "Jeffrey" 100]]`
    end
    

    But, given what you're describing, I feel like using the Table extension might be a better way. It works well with reading what are key/value pairs from lists, and those can be added dynamically at runtime. This way each turtle can have its own table of values to store and use. This also lets you maintain flexibility when working with unknown attributes; you can just access values by the keys. Here is one way to adapt your example:

    extensions [table]
    
    turtles-own [data-table]
    
    to setup
      clear-all
      let attributes ["ID" "Name" "Score"]
      let data [123 "Jeffrey" 100]
      ; this zips up our two lists into a list of lists so it's easier for Table to read them
      ; you could also use the FP extension `fp:zip` prim for this
      let zipped map [ i ->
        (list (item i attributes) (item i data))
      ] (range length attributes)
    
      create-turtles 1 [
        set data-table table:from-list zipped
      ]
    
      show [data-table] of turtles
      ; outputs `observer: [{{table: [["ID" 123] ["Name" "Jeffrey"] ["Score" 100]]}}]`
    end