Skip to content

NetLogo/FP-Extension

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

24 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

NetLogo Functional Programming Extension (fp)

Introduction:

The NetLogo functional programming extension introduces several higher order procedures commonly found in functional programming languages.


Using:

First, make sure to include the functional programming extension in the model as such:

extensions[fp]

If your model already uses other extensions, then it already has an extensions line in it, so just add fp to the list.

For more information on using NetLogo extensions, see the Extensions Guide.

When to use:

In general, anything you can do with the functional programming extension in NetLogo, could also be done using the default primitives in NetLogo. However, using the functional programming extension would allow you to take advantage of some common higher order procedures commonly found in other functional programming languages that are not included in the default primitives in NetLogo.


Demo Model:

Functional Programming Example


Primitives:

fp:take fp:drop fp:scan fp:compose fp:pipe fp:curry fp:find-indices fp:find fp:flatten fp:zip fp:unzip fp:iterate fp:iterate-last


fp:take

fp:take number list

Accepts a positive integer number and a list. Returns a list consisting of the first number elements in list, or returns the entire list if number is more than the number of elements in the given list. If number is not an integer, it is rounded to the closest integer.

Examples:
; Take the first 2 elements in the list ["a" "b" "c"]
fp:take 2 ["a" "b" "c"]
=> ["a" "b"]

; Take the first 5 elements in the list [0 1 ... 9]
fp:take 5 (range 10)
=> [0 1 2 3 4]

fp:drop

fp:drop number list

Accepts a positive integer number and a list. Returns a list consisting of all the elements of the list except the first number elements, or returns an empty list if number is more than the number of elements in the given list. If number is not an integer, it is rounded to the closest integer.

Examples:
; Drop all but the last 2 elements in the list ["a" "b" "c"]
fp:drop 2 ["a" "b" "c"]
=> ["c"]

; Drop the first 5 elements in the list [0 1 ... 9]
fp:drop 5 (range 10)
=> [5 6 7 8 9]

fp:scan

fp:scan reporter list

Accepts a binary reporter (arity 2) and a list as parameters and will use the reporter to collapse the elements from list to return a list consisting of the running total from each element. That is, the first two elements in list are operated upon by the reporter and the resultant of that operation becomes the first element of the list returned. This value is then used along with the next element in the list to obtain the second element in the resulting list and so on until the entire list is used.

Example:
; Applies the addition operation to the given list starting from the beginning and returns a list consisiting of all the resulting values
fp:scan + [1 2 3 4 5]
=> [1 3 6 10 15]

; Applies the given anonymous reporter to the given list starting from the beginning and returns a list consisiting of all the resulting values
fp:scan [[a b] -> a] [1 2 3 4 5]
=> [1 1 1 1 1]

fp:compose

fp:compose reporter1 reporter2

(fp:compose reporter1 ...)

Accepts a minimum of two reporters and returns a single reporter which combines the given reporters. When the resulting reporter is called, the last given reporter is evaluated and its result is passed as the argument to the previous reporter until the first reporter is evaluated, producing the output. Every given reporter except for the last one must be unary (accept only one argument).

Example:
; Combines the two given reporters. The resulting reporter first adds 5 to x and then divides it by 2.
let f fp:compose [x -> x / 2] [x -> x + 5]
(runresult f 3)
=> 4

; Combines the three given reporters. The resulting reporter first adds the given arguments, then takes the absolute value of the addition and multiplies it by 10.
let g (fp:compose [x -> x * 10] abs +)
(runresult g -3 2)
=> 10

fp:pipe

fp:pipe reporter1 reporter2

(fp:pipe reporter1 ...)

This primitive is the same as the compose primitive, except the reporters are evaluated in reverse order. Just like compose, it accepts a minimum of two reporters and returns a single reporter which combines the given reporters. When the resulting reporter is called, the first given reporter is evaluated and its result is passed as the argument to the next reporter until the last reporter is evaluated, producing the output. Every given reporter except for the first one must be unary (accept only one argument).

Example:
; Combines the two given reporters. The resulting reporter first divides x by 2 and then adds 5.
let f fp:pipe [x -> x / 2] [x -> x + 5]
(runresult f 4)
=> 7

; Combines the three given reporters. The resulting reporter first adds the given arguments, then multiplies the result by 10 and finally takes the absolute value of the result.
let g (fp:pipe + [x -> x * 10] abs)
(runresult g -3 2)
=> 10

fp:curry

fp:curry reporter value

(fp:curry reporter value1 ...)

Accepts a reporter and at least one value. Returns the reporter such that the given value (or values) is now fixed to the first argument (or first n arguments) of the given reporter. This enables the user to reduce the number of arguments the given reporter accepts by fixing or partially applying the given values to the first arguments of the reporter. If the number of values given exceeds the number of arguments the reporter can take, only the first n values will be applied where n is the arity of the reporter.

Example:
; Defines a function g that takes three arguments. Use curry to define a new function f that takes two argemunts, where the first argument of g is fixed to 1.
to-report g [x y z]
  report x + y + z
end
let f fp:curry g 1
(runresult f 2 3)
=> 6

; Curry the given values to the anonymous reporter, but only the first value can be fixed because the reporter accepts only one value.
(runresult (fp:curry [x -> x + 1] 1 2))
=> 2

fp:find-indices

fp:find-indices reporter list

Accepts a Boolean reporter and a list. Returns the indices of every element in the given list that reports True for the given reporter. Returns an empty list if the reporter doesn't report True for any of the items in the given list. The reporter must be a Boolean reporter.

Example:
; Finds the indices of the elements in the given list that are equal to 2.
fp:find-indices [x -> x = 2] [1 2 1 2]
=> [1 3]

; Finds the indices of the elements in the given list that are alpha-numerically less than the symbol "b".
fp:find-indices [x -> x < "b"] ["b" "a" "b" "c" "a"] => [1 4]

fp:find

fp:find reporter list

Accepts a Boolean reporter and a list. Returns the first element of the given list that reports True for the given reporter. Returns False if the reporter doesn't report True for any of the items in the given list. The reporter must be a Boolean reporter.

Example:
; Find the first element in the given list that starts with a "t"
fp:find [ s -> first s = "t" ] ["hi" "there" "everyone"]
=> "there"

; Find the first element in the given list that is equla to 3. Since there are none, it returns False.
fp:find [x -> x = 3] [1 2 1 2]
=> False

fp:flatten

fp:flatten list

Accepts a list and returns the given list such that none of the elements in the list are enclosed in lists. It flattens the given list until there are no lists in it.

Example:
fp:flatten [[1 2 3][4 5]]
=> [1 2 3 4 5]

fp:flatten [[[1 [2] 3]][[4 [5]] [6]]]
=> [1 2 3 4 5 6]

fp:zip

fp:zip list1 list2

(fp:zip list1 ...)

Accepts a minimum of one list as its argument and returns a list of tuples where the nth item in each sublist of the given list are paired together with each other, creating a new list of tuples from the given lists. If the given lists are not the same length, the number of tuples returned will be the length of the shortest given list.

Example:
; Create a new list from the given two lists where the ith items are paired together
fp:zip [1 2 3] [4 5 6]
=> [[1 4] [2 5] [3 6]]

; Create a new list from the given three lists where the ith items are collected together
(fp:zip [1 2] [3 4] [5 6])
=> [[1 3 5] [2 4 6]]

fp:unzip

fp:unzip list

This is similar to zip, except it accepts a list of lists as its argument. Returns a list of tuples where the nth item the given list of lists are paired together with each other, creating a new list of tuples from the given list of lists. If the given list contains lists of different lengths, the length of the tuples in the returned list will be different accordingly.

Example:
; Create a new list from the given list of lists where the ith items in the sublists are paired together
fp:unzip [[1 4] [2 5] [3 6]]
=> [[1 2 3] [4 5 6]]

; Create a new list from the given list of lists where the ith items in the sublists are paired together. In this case the sublists in the given list are of different lengths, so the sublists in the list that is returned are also of different lengths.
fp:unzip [[1 "a"] [2 "b"] [3 "c"] [4]]
=> [[1 2 3 4] ["a" "b" "c"]]

fp:iterate

fp:iterate-last

fp:iterate reporter initial-value repetitions

fp:iterate-last reporter initial-value repetitions

Runs the reporter on the given initial value a number of times equal to the given repetitions. If you run fp:iterate it will report the results as a list, including the original intial value. If you run fp:iterate-last only the final value will be reported. It is recommended to use fp:iterate-last instead of last fp:iterate when possible as the latter will generate an intermediate list which could slow a model down when using a large number of repetitions.

The reporter given must take a single argument or else a runtime error will be generated.

Examples:
; Calculate the first 6 powers of two:
fp:iterate [[a] -> a * 2] 1 5 => [1 2 4 8 16 32]
fp:iterate-last [[a] -> a * 2] 1 5 => 32

; Add more and more exclamation points:
fp:iterate [[a] -> (word a "!")] "hey" 5 => ["hey" "hey!" "hey!!" "hey!!!" "hey!!!!" "hey!!!!!"]
fp:iterate-last [[a] -> (word a "!")] "hey" 5 => "hey!!!!!"

; Alternate true/false values:
fp:iterate [[a] -> not a] true 5 => [true false true false true false]
fp:iterate-last [[a] -> not a] true 5 => false