Out of curiosity, I was trying to find a way to combine several inputs into a single input window (currently set to string) on the interface. In the current example, these inputs are the 4 parameters necessary for resize-world.
While my result works, I would be interested to hear if someone else knows a more elegant solution. I specifically dislike the use of item here.
Interface:
input-window
[0 5 0 5]
Code:
to setup
let worldsizes runresult input-window
resize-world item 0 worldsizes item 1 worldsizes item 2 worldsizes item 3 worldsizes
end
Using this post as base, I created a procedure that allows a command, both anonymous and regular, to take a single list of arguments as input.
Interface:
input-window
[0 5 0 5]
Code:
to test
let worldsize run-result input-window ;creates a list
apply-command "resize-world" worldsize
end
to apply-command [command inputlist]
; Takes two inputs
; 1: Anonymous command/string containing anonymous command/string containing regular command
; 2: A list of inputs, corresponding in length to the number of inputs the command takes
; Applies the command with the different items of the list as input
; Heavily inspired by stackoverflow-user Bryan head
set command anonimify-command command length inputlist ; Using non-anonymous commands gave issues, hence they are anonimified
(run listify-command command length inputlist inputlist)
end
to-report listify-command [ command num-args ]
; Takes an anonymous command and the length of a list as input
; Outputs an anonymous command that takes the different items of the list as inputs
; Heavily inspired by stackoverflow-user Bryan head
if (not is-anonymous-command? command) [error "The first input for listify-command has to be an anonymous command"]
let args (reduce word n-values num-args [ x -> (word " item " x " ?") ])
;show (word runresult (word "[ ? -> (run command " args ") ]"))
report runresult (word "[ ? -> (run command " args ") ]")
end
to-report anonimify-command [ command number-of-inputs]
; Takes two inputs
; 1: Takes an anonymous command, a string containing an anonymous command or a string containing a regular command
; 2: Takes a number that corresponds to the number of inputs the anonimified command should take
; Returns anonymous commands unaltered
; Returns strings containing an anonymous command as an anonymous command
; Returns strings containing a regular command as an anonymous command
if (is-anonymous-command? command) [ ; Anonymous commands get returned
report command
]
if (is-string? command) [ ; Strings are processed
carefully [ ; Using run-result on a string that does not contain an anonymous command causes an error, hence carefully
if (is-anonymous-command? run-result command) [
set command run-result command
]
]
[
let inputs n-values number-of-inputs [ i -> word " x" i]
let inputs-as-string reduce word inputs
let command-string (word "[ [" inputs-as-string " ] -> " command inputs-as-string" ]")
set command run-result command-string
]
report command
]
error "The inputted command must be either an anonymous command, a string containing an anonymous command or a string containing a command"
;If the input is neither anonymous command nor string, an error is displayed
end
Related
I've been trying since yesterday to code the following:
to output
file-open ( word NameOutfile-output ".csv" )
ifelse Integer? = true ; interger switch in GUI
[file-print ( word "id;my_xcor;my_ycor" ) file-print ( word self " ; " round my-xcor " ; " round my-yco )]
[file-print ( word "id;my_xcor;my_ycor" ) file-print ( word self " ; " my-xcor " ; " my-ycor )]
file-close
end
I have non-integer coordinate values. I would like to put a switch on the interface if it is turned on it will generate an output with integer values. But, it is giving the following error: ROUND expected input to be a number but got the list [1 2 1 1 1] instead.
I don't know how to close a created .csv file. For example: I export a .csv table. If I run it again, the model will save the results in the table already created. I wish that didn't happen. It is possible?
As you said, NetLogo tells you that round wants a number as input but you're giving it a list.
You can fix this by using map, which lets you run reporters on all elements of a list (and round is a reporter indeed):
to round-my-list
let my-list [3.1 5.6 8.32]
let my-list-rounded (map round my-list)
print my-list-rounded
end
What you are experiencing with the file is the standard behaviour. From the NetLogo Dictionary at file-open, you can read this:
When opening a file in writing mode, all new data will be appended to the end of the original file. [...] If you don't want to append, but want to replace the file's existing contents, use file-delete to delete it first, perhaps inside a carefully if you're not sure whether it already exists.
So if you want every time a new file, you should add something to your code that first tells to delete that file; then, write it again. Using file-delete and carefully in combination as suggested in the NetLogo Dictionary:
to output
carefully
[file-delete ( word NameOutfile-output ".csv" )]
[]
file-open ( word NameOutfile-output ".csv" )
ifelse Integer? = true ; interger switch in GUI
[file-print ( word "id;my_xcor;my_ycor" ) file-print ( word self " ; " round my-xcor " ; " round my-yco )]
[file-print ( word "id;my_xcor;my_ycor" ) file-print ( word self " ; " my-xcor " ; " my-ycor )]
file-close
end
I am trying to read a .txt file containing strings:
Delivery LHR 2018
Delivery LHR 2016
Delivery LHR 2014
Delivery LHR 2011
Delivery LHR 2019
Delivery LHR 1998
I have tried below codes but not working. It reported "expect a literal value" when running file-read
globals [input]
to setup
set input []
file-open "test.txt"
while [not file-at-end?]
[
let a quote file-read
let b quote file-read
set input lput a input
set input lput b input
print input
]
file-close
end
to-report quote [ #thing ]
ifelse is-number? #thing
[ report #thing ]
[ report (word "\"" #thing "\"") ]
end
You can kind-of get what you want with the csv extension which comes with NetLogo. It at least let's you specify a delimiter, so " ", but you'll have to manually read past all the blank columns it'll see.
extensions [csv]
globals [input]
to setup
set input []
let lines (csv:from-file "test.txt" " ")
foreach lines [ line ->
let col1 (item 0 line)
let i 1
while [item i line = ""] [ set i (i + 1) ]
let col2 (item i line)
show col2
set i (i + 1)
while [item i line = ""] [ set i (i + 1) ]
let col3 (item i line)
show col3
set input lput col1 input
]
show input
end
The reason it doesn`t work can be found in the file-read description from the NetLogo Dictionary Manual (https://ccl.northwestern.edu/netlogo/docs/dictionary.html#file-read)
[...]Note that strings need to have quotes around them.[...]
It is not a solution to add the quotes within NetLogo because file-read already throws an Error, if the next entry in the file is not one of number, list, string, boolean, or the special value nobody. And string in this case means, it needs to have quotes around it.
Thus, to read the file into NetLogo you have to put quotes around the strings in your input file. Alternatively, if the strings in your input file always have the same length, you could try to read the file using the primitive file-read-characters. Here is an example that should work with your input file:
to setup
file-open "test.txt"
while [not file-at-end?]
[
let a file-read-characters 8
let skip file-read-characters 4
let b file-read-characters 3
let c file-read
print (list a b c)
]
file-close
end
I have some code that reads a file of names and creates a list:
let who-file-name "world-health-field-surveillance.csv"
let who-file-name-dict csv:from-file who-file-name
let who-file-names sort [who] of names
let index 1 ;not 0, this removes the header in the csv
repeat length who-file-names [
file-open "world-health-field-surveillance.csv"
if file-at-end? [stop]
let entry (item 0 (item index who-file-name-dict))
if entry = "\n" [stop]
The file might end with some blank lines or its possible the file has names separated by a newline, like so:
Allman
Atkinson
Behlendorf
I want to ignore any lines that contain only whitespace.
My sample code doesn't work.
How could I do this in netlogo?
What are you trying to do, exactly? If I have a csv file that looks like this:
If I run this code:
extensions [ csv ]
to setup
ca
let example csv:from-file "example_names.csv"
print example
reset-ticks
end
I get a list output that looks like:
[[Allman] [Atkinson] [Behlendorf] [Belnich] [Cravit] [Court]]
Is that not what you're after? If you need just a single-level list, you can do
print reduce sentence example
to get
[Allman Atkinson Behlendorf Belnich Cravit Court]
I'm looking for a way to truly alphabetize a list. Assuming it's a list of basic words, such as:BlackGreenThe RedBlueWaxyLivingPorousSolidLiquidVioletIs there a way to modify this code to alphabetize the list where "The Red" comes before "Solid"? Here's what I have so far:
SaveVar=%ClipboardAll%
Clipboard=
Send ^c
ClipWait, 0.5
Sort clipboard, CL
;Process exceptions
Sort := RegExOmit (Sort, "The")
Send ^v
Sleep 100
Clipboard=%SaveVar%
SaveVar=
return
Write a custom comparison function that ignores the starting "The " substring.
list = Black`nGreen`nThe Red`nBlue`nWaxy`nLiving`nPorous`nSolid`nLiquid`nViolet`nThe Azure
Sort , list , F Compare
MsgBox, %list%
Compare( a , b )
{
arem := RegExReplace(a, "A)The " , "" )
brem := RegExReplace(b, "A)The " , "" )
return arem > brem ? 1 : arem < brem ? -1 : 0
}
Regular expressions are used to remove the substring "The " from the string and the result stored in a temporary string, which is then used for comparison.
The substring must start at the beginning of the string, regex option A), and must include a space immediately after The.
I am using the Red binding to read and write files, and the hard-coded-file-names version works well. But I want to get file names from the command line dynamically. As Red has no such utility right now. So I try to make it with Red/System. I can get command line args now, but I don't know how to pass it to the Red part. Like the example below, I need to pass source-file and target-file to read and write:
Red []
#include %input-output.red
#system-global [
args: system/args-list
args: args + 1
source-file: args/item
args: args + 1
target-file: args/item
print [source-file target-file ]
]
data: read source-file
probe data
write target-file data
Something like this should work, just convert your #system-global code to a routine function:
Red [
File: "test-read.red"
]
read-arg: routine [
files [block!]
/local
str [red-string!]
args [str-array!]
][
args: system/args-list
args: args + 1
str: string/load symbol/duplicate args/item 1 + length? args/item UTF-8
block/rs-append files as red-value! str
args: args + 1
str: string/load symbol/duplicate args/item 1 + length? args/item UTF-8
block/rs-append files as red-value! str
]
probe read-arg []
Then once compiled, you should get this result:
C:\Dev\Red>test-read fileA fileB
["fileA" "fileB"]
Presuming that the arguments to the read and write functions are string!, you will need to write a routine that will return a red-string! of a Red/System c-string!.
The Red API is understandably not documented yet as Nenad is busy working on getting to Red 1.0. This is a function which goes the other way, string! to c-string! that might help show some light on what is needed - https://github.com/PeterWAWood/Red-System-Libs/blob/master/UTF8/string-c-string.reds .
There may be an easer way to use the Red api that somebody else can suggest.