Conventional Programming 1
Do you have questions or comments about this model? Ask them here! (You'll first need to log in.)
## WHAT IS IT?
This is a tutorial introduction to NetLogo as a conventional programming language. It is self-contained on the assumption that the user is passingly familiar with the NetLogo platform and its use for agent-based modeling. Before tackling this tutorial, users should at the very least read the tutorials in the NetLogo user manual and exercise some NetLogo programs. Readers should know about patches, turtles, and the world in NetLogo, although we will not discuss them in this tutorial. Readers should also know about the Interface tab and the things that can go on it, the Info tab (where you are now), and the fact that NetLogo code resides in the Code tab (as well as in some of the interface widgets).
## CONTEXT
The NetLogo platform comes with a programming language, which we may as well call NetLogo. Together, the platform and the programming language, afford and have been designed for building agent-based models. Not every programming task, however, calls for an agent-based model. Most in fact do not and for these there are various other programming languages that, for a given task, will likely be more suitable than NetLogo. Important examples of such languages include (in no particular order) Python, PHP, C, Java, Ruby, and MATLAB. There are very many others as well.
With very few exceptions any programming language will have conventional or general programming capabilities, plus special features or capabilities that distinguish it for particular purposes. Programming languages, like all other cultural objects, must compete for acceptance and popularity. They do this largely through specialization combined with some degree or other of conventional programming capabilities. NetLogo is no exception.
NetLogo leaves much to be desired as a conventional programming language. (It is terrific in its specialization for "low floor, high ceiling" agent-based modeling.) Even so, there are a number of good reasons to explore this aspect of NetLogo.
1. Programming novices can easily obtain skills for very many useful programming tasks, such as implementing mathematical functions and getting them to calculate useful results. If you are just starting to program and need a rudimentary conventional programming language, NetLogo may do you just fine.
2. It is well known that learning one conventional programming language makes learning another much easier and faster. Having become familiar with NetLogo as a conventional programming language will help you a lot when you need to acquire facility with Python, PHP, C, Java, MATLAB, and so on.
3. Experienced programmers, those with good knowledge of one or more conventional programming languages, will find that understanding NetLogo's treatment of conventional programming helps them ramp up quickly to NetLogo and provides useful insights into how it works.
4. It will be very often the case that agents or patches in a particular model require services of a conventional programming nature. (Agents need to evaluate mathematical functions, too.) Understanding this aspect of NetLogo is essential for meeting these requirements.
Then here we go.
## HELLO WORLD
It is a tradition and a fine one to begin instruction for any programming language by showing how to write a "Hello, world!" program. Why not?
There are a number of ways to do this in NetLogo. First, type
>print "Hello, world!"
in the Command Center command line (with "observer>" to the left), and then press the Enter/Return key. You will see
>Hello, world!
appear in the Command Center display window. Why does one expression have "Hello, world!" in quotes and the other not? Well, the `print` command expects to receive a sequence of characters (called a **string**), which it will print. NetLogo indicates the extent of a string by enclosing it in double quote marks (characters). The output doesn't have quotes because they are not part of the string that `print` was asked to print.
Suppose we want to include double quote characters in the print string, how do we do that? Answer: by **escaping** them with the backslash character: `\`. Here's an example:
>observer> print "\"Hello, world!\""
>"Hello, world!"
OK, smart guy, now what if we want to include a backslash character in the output string, how do we do that? With another backslash:
observer> print "This is how you print a \\ character."
This is how you print a \ character.
And this, I submit, resolves the (apparent) paradox of using characters to delineate sequences of characters.
NetLogo also lets you print to the Interface output widget if it is present:
observer> output-print "You can print \"s and \\s to the output widget."
This yields
You can print "s and \s to the output widget.
in the output widget.
Output will accumulate unless you clear it with `clear-output` or something stronger, such as a `clear-all` command. There is a button for this on the Interface tab.
Finally, we can print from a **command procedure** stored in the Code tab:
to yo-button
output-print "Yo, world!"
end
Click the yo-button on the Interface tab and see the message printed in the output widget.
We see here the basic template for a command procedure in NetLogo:
>to _command name_
> `[` _NetLogo code_ `]`
>`end`
Of which, much more as we proceed.
## FRAMEWORK
Conventional programming languages, including NetLogo, will all (more or less) have similar or analogous features of seven kinds:
1. variables
2. operators and built-in functions
3. procedures
4. flow control
5. data structures
6. I/O (input and output)
7. special libraries/functions
We'll now work through the framework in the context of NetLogo.
## 1. VARIABLES
Variables in a programming language are symbols that may be (and normally are) assigned values. For example, in (the NetLogo code)
let sara 23.4
the variable is `sara`, the value it is assigned is 23.4 and `let` instructs NetLogo to effect the assignment. Now let's consider this line of code in a command procedure, it's typical context. (You will find this procedure in the Code tab.)
to local-assignment
let sara 23.4
output-print (word "The value of sara is " sara ".")
set sara 43.2
output-print (word "Now the value of sara is " sara ".")
end
Try clicking the local-assignment button on the Interface tab. What's going on here is this. First, `let sara 23.4` creates a **local variable** and assigns it the value 23.4. Next, `output-print (word "The value of sara is " sara ".")` uses `(word "The value of sara is " sara ".")` to create the string "The value of sara is 23.4." and then print it to the output widget. Next, `set sara 43.2` assigns `sara` a new value. Because `sara` already exists (having been created with `let sara 23.4`), NetLogo requires us to use `set` instead of `let` to assign values to variables. Finally, `output-print (word "Now the value of sara is " sara ".")` uses `(word "Now the value of sara is " sara ".")` to create the string "Now the value of sara is 43.2." and prints it to the output widget.
`sara` in this example is a **local variable**, meaning that it was created with `let` within a procedure and is only valid within that procedure. If we create another procedure and try to use `sara` NetLogo will go into error mode. If we use `let` in another procedure to declare a variable called `sara` that variable is different than the one we declared and assigned in our other procedure, `local-assignment`. Further, when we assign this new `sara` a value, the old `sara` is unaffected. They are two distinct entities having the same name. Think: Grace Lee who made an entire movie, "The Grace Lee Project," by interviewing other people named Grace Lee. Here's a link:
http://www.wmm.com/filmcatalog/pages/c646.shtml
(It's a fine movie and will help you remember how local variables work.)
Now, if we have **local** variables, do we also have variables that are not local? Definitely, and they are called **global variables**. There are two kinds in NetLogo. One kind is declared at the top of the Code tab using the NetLogo key word `globals`. For example,
globals [bob carol ted alice]
declares the variables `bob`, `carol`, `ted`, and `alice` and makes them global. The effect of this is to make each of these variables available throughout the NetLogo program. Consider the following two command procedures, which you will find in the Code tab.
to bob-plus-one
set bob (bob + 1)
output-print (word "The value of bob is now " bob ".")
end
to bob-plus-two
set bob (bob + 2)
output-print (word "The value of bob is now " bob ".")
end
(Notice: (i) We have used the addition operator, `+`, to add two numbers. In NetLogo, it is required that operators have spaces on either side, just as they do here. (ii) `word` is also a kind of operator. It combines sequences of strings and/or numbers into a single string. We will have more to say below about operators.)
If you click the clear-all button on the Interface and then click
1. bob-plus-one
2. bob-plus-one
3. bob-plus-two
4. bob-plus-one
You will see displayed in the output widget the following.
The value of bob is now 1.
The value of bob is now 2.
The value of bob is now 4.
The value of bob is now 5.
Notice that the value of `bob` **before** you first clicked on bob-plus-one must have been zero (why?). In general, variables declared with `globals` are initialized to 0.
The second way to create a global variable is with a widget on the Interface. On the Interface tab you will find a slider called a-global-slider-variable and in the Code tab you will find this command procedure.
to show-global-slider-value
output-print (word "The value of a-global-slider-variable is "
a-global-slider-variable ".")
end
Move the slider to a convenient value then click the show-global-slider-value button. The value of a-global-slider-variable will be displayed in the output widget.
The following command procedure is also in the Code tab.
to set-global-slider-value-to-bob
set a-global-slider-variable bob
output-print (word "The value of a-global-slider-variable is "
a-global-slider-variable ".")
end
Click the show-global-slider-value button. The value of a-global-slider-variable will be set to whatever value the variable `bob` and the new value of a-global-slider-variable will be displayed in the output widget. Notice that the slider will also change to its new value. Also notice that we broke the `output-print` statement into two lines, breaking at a point between items in the `word` list. In general, NetLogo is relaxed about formatting. Go ahead and break lines to keep everything visible in the window, but I recommend using indentation (as above), to maintain clarity.
Variable names in NetLogo should begin with alphabetic characters (a-z), may contain digits (0-9), and both the dash (-) and the underscore (_). Spaces are **NOT** allowed. Also, NetLogo is not case-sensitive, so `BOB` and `bob` and `BoB` and so on are all equivalent and may be used interchangeably (although it would be ill-advised to do this).
## 2. OPERATORS AND BUILT-IN FUNCTIONS
You are familiar with the concept of a function from your acquaintance with mathematics. A function takes as input one or more values (or variables having values) and returns a value. The function concept in conventional programming languages is essentially the same.
NetLogo comes with a number of built-in functions, which you get to use at will. For example, there are trigonometric functions.
observer> print cos(12.3)
0.9770455744352636
Look, for other examples, under Math in the NetLogo Dictionary. Notice the syntax for our example. `cos` is our function name (short for cosine). The function takes one input value, or **argument**, which in the example is 12.3. In general, function arguments should be individually enclosed in parentheses, as we see in this case.
An **operator** is just a function with a different syntax. `+` as we saw above is an operator. In standard prefix notation, standard for functions, we might write `+(3.4,6.7)`. Instead, using operator infix notation we write `3.4 + 6.7`. Any differences are entirely superficial. Again, see the Math section in the NetLogo Dictionary for examples of operators in NetLogo.
## 3. PROCEDURES
Procedures in a conventional, or general, programming language are distinct "chunks" of code that are named and may be run (executed) by "calling" them from another part of the program. Computer programs easily become very complicated and require ways to modularize them (break them into smaller, meaningful pieces) if they are to remain manageable and usable. Procedures are the most fundamental form of modularization.
NetLogo recognizes two kinds of procedures:
1. command procedures (or just commands)
2. reporter procedures (or just reporters)
The key difference between them is that reporters return values when they are called. There are very much like functions. Indeed that is how we should think of them. Commands, on the other hand do things (including calling reporters).
We have already seen several examples of command procedures, so here is a reporter procedure. You will find it in the Code tab.
to-report square-em-add-em [x y]
; This reporter takes two variables when called,
; x and y, and returns the value of x^2 + y^2.
let xx x * x
let yy y ^ 2
report xx + yy
end
This reporter illustrates the general pattern for reporters in NetLogo.
to-report
report
end
The reporter name should follow the conventions for naming variables (as should the names of command procedures). The parameter list is set off between square brackets: [...] and is optional, although normally needed. Variable names, separated by spaces, go into the list. You should use names that are not otherwise used for global variables. These names will become local variables in the reporter procedure. In our present example, they are `x` and `y`. It is fine to use `x` and `y` as parameters in other procedures. Just remember that they are local variables. Remember: Grace Lee.
The first two lines of our procedure begin with semicolons: `;`. NetLogo uses the semicolon as a comment sign. Everything in a line after a semicolon (unless it appears in a quoted string, as in `print "We can print ;s if they appear within strings"`) is considered to be a comment by NetLogo and is ignored. Comments are very handy things. Use them profusely.
`let xx x * x` creates a new local variable, `xx` and assigns it the value of x2. Similarly, `let yy y ^ 2` creates a new local variable, `yy` and assigns it the value of y2.
Finally, every reporter procedure requires at least one `report` command. Notice that NetLogo allows complex expressions that get evaluated as part of the `report` command.
In calling a reporter we recommend enclosing its arguments singly in parentheses. Here's an example, which you will find in the Code tab. There is also a button for it on the Interface tab.
to call-square-em-add-em
let x1 a-global-slider-variable
let x2 another-global-slider-variable
let daResult square-em-add-em(x1)(x2)
output-print (word "The result is " daResult ".")
end
Reporter procedures may be called from the command line of the Command Center, from command procedures, from reporter procedures, and indeed from buttons on the Interface tab.
## 4. FLOW CONTROL
Also known as control flow, and called Control Flow and Logic in NetLogo. See "Control/Logic" in the NetLogo Dictionary.
Absent flow control statements, execution of programming language statements proceeds sequentially, from top to bottom. The examples so far illustrate this. Very often, however, we wish to control the flow of execution, perhaps to re-execute a number of lines of code, perhaps to skip certain lines of code, and so on. Flow control statements let us do this. NetLogo supports versions of the basic flow control constructs found in conventional programming languages.
The `if` statement has the following syntactic template.
>`if` _condition_ `[` _commands_ `]`
where _condition_ is a NetLogo expression that evaluates to `true` or `false` and _commands_ indicates the NetLogo code that is to be executed if the _condition_ evaluates to `true`. Here's an example, which you will find in the Code tab.
to simple-if
if a-global-slider-variable > 17
[output-print "It's more than 17."
set a-global-slider-variable 17]
end
Try it out by clicking on the simple-if button on the Interface tab. Comments:
1. `a-global-slider-variable > 17` evaluates to `true` or `false`, depending on the current value of `a-global-slider-variable`, e.g.,
observer> print a-global-slider-variable > 17
true
2. The `>` symbol is a NetLogo operator (that is it is a function presented in infix form) and it means just what you think it means: It corresponds to the mathematical "greater than" sign, so that `x > y` evaluates to `true` exactly when the values of `x` and `y` are numeric (or can be interpreted as such) and the value of `x` is strictly larger than that of `y`.
3. If `a-global-slider-variable > 17` evaluates to `false`, then the associated code is skipped and execution continues after the `if` statement.
4. NetLogo requires that the associated code be enclosed in square brackeks, `[...]`, but it is very lenient regarding just where they are placed. The example shows a stylistically sound placement.
5. You may put any number of lines of code, including flow control statements, within the square brackets.
to simple-ifelse
ifelse a-global-slider-variable > 17
[output-print "It's more than 17."
set a-global-slider-variable 17]
[output-print "It's less than or equal to 17."]
end
The `ifelse` statement has the following syntactic template.
> `ifelse` _reporter_ `[` _commands1_ `]` `[` _commands2_ `]`
where _reporter_ is a NetLogo expression that evaluates to `true` or `false`, _commands1_ indicates the NetLogo code that is to be executed if the _reporter_ evaluates to `true`, and _commands2_ indicates the NetLogo code that is executed if the _reporter_ evaluates to `false`. So, why _reporter_ and _condition_ earlier for the `if` statement? The NetLogo document is simply confused here and the terminology is a bit confus_ing_. We've seen how to write reporter procedures. Any such procedure counts as a reporter in NetLogo, but other things count as reporters as well. Expressions with relational operators, as in `a-global-slider-variable > 17`, count as reporters. (See Math in the NetLogo Dictionary for the full list of relational operators in NetLogo.) NetLogo also has a number of built-in reporters that evaluate to `true` or `false` and are often very useful, including:
>observer> print is-number? a-global-slider-variable
>true
>observer> print is-string? another-global-slider-variable
>false
>observer> print is-string? "carol"
>true
>observer> print is-string? carol
>false
The key thing to remember is that whether for _condition_ in `if` or _reporter_ in `ifelse` we need a reporter that evaluates to `true` or `false`.
Here's an example, which you will find in the Code tab.
to simple-ifelse
ifelse a-global-slider-variable > 17
[print "It's more than 17."
set a-global-slider-variable 17]
[print "It's less than or equal to 17."]
end
Try it out with the simple-ifelse button on the Interface tab.
The `repeat` command
> `repeat` _number_ `[` _commands_ `]`
runs _commands_ _number_ times.
Here's an example, which you will find in the Code tab.
to simple-repeat
let dacount 0
repeat a-global-slider-variable
[output-print dacount
set dacount (dacount + 1)]
end
Try it out with the simple-repeat button on the Interface tab. What happens if the _number_ value is not an integer, say it's equal to 12.3? Well, edit the a-global-slider-variable widget and experiment.
With the `while` statement
>`while [` _reporter_ `] [` _commands_ `]`
if _reporter_ reports false, we exit from the loop; we run _commands_ and repeat. Unless you intend for `while` to execute indefinitely (as an "infinite loop"), you will need to change the value of a variable that can affect whether _reporter_ reports `true` or `false`. Here's an example.
to while-away
let dacount a-global-slider-variable
while [dacount >= 0] [
output-print dacount
set dacount (dacount - 1)
]
end
You will find it in the Code tab. Try it out with the while-away button on the Interface tab. Notice a slight change in formatting for this command procedure. This alternative is also fine and is in fact favored a bit by NetLogo. It's up to you.
Our final flow control statement, `foreach` is in fact **not** listed by NetLogo in the Control/Logic category in the dictionary. Instead it appears in the Task category. Go figure. Anyway, here is the template.
>`foreach` _list_ _command-task_
Here is NetLogo's explanation:
>With a single list, runs the _task_ for each item of _list_.
This isn't very helpful, but the following example is.
to simple-foreach
foreach [2 4 6 8] [
output-write ?
output-type " "
output-print ? * ?
]
end
You will find it in the Code tab. Try it out with the simple-foreach button on the Interface tab. Here's what's going on. `foreach` iterates through its _list_, going item by item. In the example, the first item is the number 2, so the NetLogo special variable `?` is assigned the value 2. Then the code in _command-task_ is executed once. (Notice `output-write` to print without a new line, and `output-type` to print a blank space. Look them up in the NetLogo Dictionary.) After the code in _command-task_ is executed, control flows back to the start of the `foreach` statement and `?` is assigned the value of the next item in _list_, which is 4 the second time through, and then the code in _command-task_ is executed once with the new value of `?`. This continues until each item in _list_ has been assigned in tern to `?`, after which the flow of control is passed to the next statement after the `foreach` statement.
A problem with the `foreach` construction we just looked at is that we explicitly state the _list_. This is fine for small lists, but what if we want to iterate over hundreds or thousands (or more) items? For this we can construct _list_ using
>`n-values` _size_ _reporter-task_
which reports "a list of length _size_ containing values computed by repeatedly running the _task_." The NetLogo documentation isn't as articulate as one might hope. Here's an example showing how to do it.
to n-values-foreach
foreach n-values 4 [(1 + ?) * 2] [
output-write ?
output-type " "
output-print ? * ?
]
end
As usual, you will find it in the Code tab, should try it out with the n-values-foreach button on the Interface tab. Notice that `n-values-foreach` and `simple-foreach` have identical outputs. Note as well:
>observer> print n-values 5 [?]
>[0 1 2 3 4]
>observer> print n-values 5 [1 + ?]
>[1 2 3 4 5]
>observer> print n-values 5 [(1 + ?) * 2]
>[2 4 6 8 10]
See the pattern?
Finally, the topic of **nested loops**. Flow control statements may, and often need to, appear within the scope of other flow control statements. This presents a problem in the case of NetLogo's `foreach` because we only have the one iteration variable, `?`. In a nested `foreach`, what does a particular `?` refer to? Is it the first `foreach`? the second? the third? You see the point. NetLogo has a finesse for the problem, which you can read about in the documentation (and which will not work for the example I am giving). I'll show you the alternative I prefer, which in any case is fully general.
Suppose we want to print out something like a multiplication table, but one in which the row values are multiplied by the square roots of the column values. The resulting table will not be symmetric. Moreover, let us assume a table with 6 rows and 5 columns.
to nested-foreach
foreach n-values 6 [(1 + ?)] [
let x1 ?
foreach n-values 5 [(1 + ?) ^ 0.5] [
let x2 ?
output-write x1 * x2
output-type " "
]
output-print " "
]
end
As usual, you will find it in the Code tab, and you should try it out with the nested-foreach button on the Interface tab. The trick, or method, in `nested-foreach` is to assign `?` to a local variable immediately after it is itself assigned a value. Then, with every instance of nesting, we assign a **new** local variable the value of `?`. And we do our computations in terms of these local variables---here `x1` and `x2`---instead of `?` which has no global unique interpretation.
## 5. DATA STRUCTURES
So far, we have worked with just two kinds of data: numbers and strings. NetLogo variables can hold either and a single variable can have the type of data assigned to it changed arbitrarily.
Think of a data structure as a complex of simpler data items, for which the programming language as commands for accessing it and manipulating it.
The primary data structure in NetLogo (aside from turtles and patches, which we ignore here) is the **list**. A list is simply an ordered sequence of items. These items may be of any sort, including numbers, strings, other lists. (And turtles and patches, but not now.) A given list may include any or all of these kinds of things. So lists are very flexible and hence powerful data structures.
See the List category in the NetLogo Dictionary for the commands associated with lists. Our treatment here is introductory.
The empty list---a list with nothing in it---is indicated by `[]`. You add something to the beginning of a list with the `fput` command and to the end with `lput`. Items in lists are separated by spaces (**NOT** commas).
>observer> set bob []
>observer> print bob
>[]
>observer> set bob fput 34.5 bob
>observer> print bob
>[34.5]
>observer> set bob lput "Carol" bob
>observer> print bob
>[34.5 Carol]
>observer> set bob fput bob bob
>observer> show bob
>observer: [[34.5 "Carol"] 34.5 "Carol"]
You can access items in a list, counting from 0, and you can retrieve the length of a list:
>observer> print item 1 bob
>34.5
>observer> print item 0 bob
>[34.5 Carol]
>observer> print length bob
>3
You can replace items in a list with other items. This creates a new list and leaves the original unaltered.
>observer> set carol replace-item 2 bob "Alice"
>observer> show carol
>observer: [[34.5 "Carol"] 34.5 "Alice"]
>observer> show bob
>observer: [[34.5 "Carol"] 34.5 "Carol"]
Similarly, you can remove items from lists.
>observer> set ted remove-item 2 carol
>observer> show ted
>observer: [[34.5 "Carol"] 34.5]
>observer> show carol
>observer: [[34.5 "Carol"] 34.5 "Alice"]
In a prototypical use of lists, we collect or create data, insert the data into a list, and return the list from a reporter. Also prototypically, we are given a list and we iterate through it, often with `foreach`, in order to process each of its items. Here's an example.
to-report cubes [alist]
; Accepts a list, alist, presumed to be numbers, and
; reports a new list of the items in alist cubed.
let toreport []
foreach alist [
set toreport lput (? ^ 3) toreport
]
report toreport
end
>observer> set bob n-values 9 [?]
>observer> show bob
>observer: [0 1 2 3 4 5 6 7 8]
>observer> print cubes(bob)
>[0 1 8 27 64 125 216 343 512]
## 6. I/O (INPUT AND OUTPUT)
We have seen a number of **output** commands, including `print`, `write`, `type`, and their `output-` analogs. See the NetLogo Dictionary for these as well as for `show`. All of NetLogo's I/O commands are gathered in the Input/output and the File categories in the NetLogo Dictionary.
We'll finish our treatment of output with a discussion of writing to files from NetLogo.
NetLogo's file reading and writing capabilities are limited, but what is available is widely useful, so here we go.
We begin with the `file-open` command. Here is the relevant passage from the NetLogo Dictionary.
> `file-open` _string_
>
>This command will interpret _string_ as a path name to a file and open the file.
>You may then use the reporters `file-read`, `file-read-line`, and
>`file-read-characters` to read in from the file, or `file-write`, `file-print`,
>`file-type`, or `file-show` to write out to the file.
>Note that you can only open a file for reading or writing but not both.
>The next file i/o primitive you use after this command dictates which
>mode the file is opened in. To switch modes, you need to close the file
>using file-close.
OK, here is a simple example.
to file-output-csv
if file-exists? "demo.csv"
[file-delete "demo.csv"]
file-open "demo.csv"
let data n-values 24 [5 + ?]
foreach [0 1 2 3 4 5] [
let row ?
let next item 0 data
file-write next
set data but-first data
foreach [0 1 2] [
let col ?
set next item 0 data
file-type ","
file-write next
set data but-first data
]
file-type "\n"
]
file-close
end
As usual, you will find it in the Code tab, and you should try it out with the file-output-csv button on the Interface tab. "csv" means "comma separated values", a common format for data in text files. Excel and indeed most statistical programs, such as R, will read it. Use this output format for your data so that these other programs can access it easily.
Here is what is in the resulting file, _demo.csv_:
>5, 6, 7, 8
>9, 10, 11, 12
>13, 14, 15, 16
>17, 18, 19, 20
>21, 22, 23, 24
>25, 26, 27, 28
Points arising:
1. For simplicity's sake I am assuming that you want to write the file into the same directory that the NetLogo program is in. If this is not correct, provide the full or relative path name, suitable for your operating system, in quotes, e.g., "../data/demo.csv". If you don't know what this means, you can probably skip the point without loss. Just keep your data files in the same directory as your NetLogo file.
2. If a file exists and has data in it when your program opens it, any data you write to the file will be appended to the end of the file. The new data will be placed after the old data. If you do not want this, then as in the example `file-output-csv` command procedure, first check to see whether your file exists and if it does, delete it. **And then** open the file.
3. The `file-type "\n"` line of code has the effect of inserting a new line character---`\n`---at the end of each row. The difference between `print` and `write` (in their various forms) is that `print` inserts a new line character and `write` does not. In code, we are outputting each line in pieces, sequentially, so we need to use `write`.
4. Once you are done with a file, issue the `file-close` command. Not doing this invites trouble.
5. Only open one file at a time in NetLogo.
See the _File Output Example.nlogo_ model in the NetLogo Models Library for more information.
Now, briefly, **file input**.
Assume we have a file, _demo-space.txt_ in the same directory as our NetLogo program and that it looks just like _demo.csv_ with spaces instead of commas:
>5 6 7 8
>9 10 11 12
>13 14 15 16
>17 18 19 20
>21 22 23 24
>25 26 27 28
Here is a simple example.
to load-input-data
ifelse ( file-exists? "demo-space.txt" )
[
let input-data []
;; This opens the file, so we can use it.
file-open "demo-space.txt"
while [ not file-at-end? ]
[
set input-data sentence input-data (list
(list file-read file-read file-read file-read))
]
output-print input-data
file-close
]
[user-message
"There is no demo-space.txt file in current directory!" ]
end
As usual, you will find it in the Code tab, and you should try it out with the load-input-data button on the Interface tab. Here is the output you will get:
>[[5 6 7 8] [9 10 11 12] [13 14 15 16]
> [17 18 19 20] [21 22 23 24] [25 26 27 28]]
Points arising:
The NetLogo command `sentence` is new for us. It appears in the List category of the NetLogo Dictionary. Here is the relevant explanation from the Dictionary:
> `sentence` _value1_ _value2_
>
>Makes a list out of the values. If any value is a list,
>its items are included in the result directly, rather than
>being included as a sublist. Examples make this clearer:
>
>show sentence 1 2
>=> [1 2]
>show sentence [1 2] 3
>=> [1 2 3]
>show sentence 1 [2 3]
>=> [1 2 3]
show sentence [1 2] [3 4]
>=> [1 2 3 4]
>show sentence [[1 2]] [[3 4]]
>=> [[1 2] [3 4]]
>show (sentence [1 2] 3 [4 5] (3 + 3) 7)
>=> [1 2 3 4 5 6 7]
1. `input-data` is defined as a local variable in this example. In any likely application you will want to access the data from the file with other parts of your program. To do this either (i) declare `input-data` as a global variable, or (ii) convert `load-input-data` to a reporter and report `input-data` upon completion.
2. The `file-read` reads the next item separated by white space in the file.
3. `(list file-read file-read file-read file-read)` reads the next for space-separated items from the file and puts them into a NetLogo list. This corresponds to a single row of the file, but we need to know this ahead of time. NetLogo does have a `read-line` command, but does not have any commands for splitting lines with commas, or spaces, or whatever.
4. We use `file-read` because there is no built-in command for dealing with CSV files.
Check out the other commands in the Files category of the NetLogo Dictionary. `user-file` and `user-new-file` allow you to prompt the user for files, rather than "hardwiring" them into your code.
See the _File Input Example.nlogo_ model in the NetLogo Models Library for more information.
And finally, NetLogo has an Interface widget for eliciting user input, called an input. There's one on the Interface tab which defines the global variable `an-input-number`. The value is editable by the user.
>observer> print an-input-number / cos(12)
>101.98869774373532
Check it out, as well as the other capabilities of input widgets.
## 7. SPECIAL LIBRARIES/FUNCTIONS
NetLogo has a lot of these, many but hardly all of which pertain directly to agent-based modeling (think: `create-turtles` and `breeds` and so on).
Especially with the extensions (see the NetLogo User Manual), there are ample additional resources for the conventional programmer, particularly arrays, hashtables, and matrices. Discussion of these will occur in subsequent numbers of this conventional programming series. Stay tuned.
## HOW TO CITE
If you mention this model in a publication, I ask that you include a citation for the model itself and for the NetLogo software:
* Kimbrough, Steven O. (2014). Conventional Programming 1 model. University of Pennsylvania, Philadelphia, PA. Conventional Programming 1.nlogo
## COPYRIGHT AND LICENSE
Copyright 2014 Steven O. Kimbrough.
![CC BY-NC-SA 3.0](http://i.creativecommons.org/l/by-nc-sa/3.0/88x31.png)
This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
Commercial licenses are also available. To inquire about commercial licenses, please contact Steven O. Kimbrough at kimbrough@wharton.upenn.edu.
Version: $Id: Conventional Programming 1.nlogo 4368 2014-10-01 21:22:32Z sok $.
Comments and Questions
globals [bob carol ted alice] to yo-button output-print "Yo, world!" end to local-assignment let sara 23.4 output-print (word "The value of sara is " sara ".") set sara 43.2 output-print (word "Now the value of sara is " sara ".") end to bob-plus-one set bob (bob + 1) output-print (word "The value of bob is now " bob ".") end to bob-plus-two set bob (bob + 2) output-print (word "The value of bob is now " bob ".") end to show-global-slider-value output-print (word "The value of a-global-slider-variable is " a-global-slider-variable ".") end to set-global-slider-value-to-bob set a-global-slider-variable bob output-print (word "The value of a-global-slider-variable is " a-global-slider-variable ".") end to-report square-em-add-em [x y] ; This reporter takes two variables when called, ; x and y, and returns the value of x^2 + y^2. let xx x * x let yy y ^ 2 report xx + yy end to call-square-em-add-em let x1 a-global-slider-variable let x2 another-global-slider-variable let daResult square-em-add-em(x1)(x2) output-print (word "The result is " daResult ".") end to simple-if if a-global-slider-variable > 17 [output-print "It's more than 17." set a-global-slider-variable 17] end to simple-ifelse ifelse a-global-slider-variable > 17 [output-print "It's more than 17." set a-global-slider-variable 17] [output-print "It's less than or equal to 17."] end to simple-repeat let dacount 0 repeat a-global-slider-variable [output-print dacount set dacount (dacount + 1)] end to while-away let dacount a-global-slider-variable while [dacount >= 0] [ output-print dacount set dacount (dacount - 1) ] end to simple-foreach foreach [2 4 6 8] [ output-write ? output-type " " output-print ? * ? ] end to n-values-foreach foreach n-values 4 [(1 + ?) * 2] [ output-write ? output-type " " output-print ? * ? ] end to nested-foreach foreach n-values 6 [(1 + ?)] [ let x1 ? foreach n-values 5 [(1 + ?) ^ 0.5] [ let x2 ? output-write x1 * x2 output-type " " ] output-print " " ] end to file-output-csv if file-exists? "demo.csv" [file-delete "demo.csv"] file-open "demo.csv" let data n-values 24 [5 + ?] foreach [0 1 2 3 4 5] [ let row ? let next item 0 data file-write next set data but-first data foreach [0 1 2] [ let col ? set next item 0 data file-type "," file-write next set data but-first data ] file-type "\n" ] file-close end to load-input-data ifelse ( file-exists? "demo-space.txt" ) [ let input-data [] ;; This opens the file, so we can use it. file-open "demo-space.txt" while [ not file-at-end? ] [ set input-data sentence input-data (list (list file-read file-read file-read file-read)) ] output-print input-data file-close ] [user-message "There is no demo-space.txt file in current directory!" ] end to-report cubes [alist] ; Accepts a list, alist, presumed to be numbers, and ; reports a new list of the items in alist cubed. let toreport [] foreach alist [ set toreport lput (? ^ 3) toreport ] report toreport end
There are 2 versions of this model.
Attached files
No files
This model does not have any ancestors.
This model does not have any descendants.