For example,
let procedure string-to-task "print ?"
(run procedure "hello")
There a couple reasons one would want to do this:
Tasks don't need to be recompiled, whereas strings sometimes do (especially when using a lot of them).
You can't pass in arguments when trying to run a string.
You sure can! Using this one weird only-obvious-in-retrospect trick:
to-report string-to-task [s]
report runresult (word "task [" s "]")
end
Note that this will return either a reporter task or a command task, depending on the contents of the input string.
Related
Why does Process.start need arguments separated in a list after every space? For example according to the docs https://api.flutter.dev/flutter/dart-io/Process/start.html
to grep a main in test.dart, we need to do
var process = await Process.start('grep', ['-i', 'main', 'test.dart']);
Won't this be a better way?
var process = await Process.start('grep', ['-i main','test.dart','-other options']);
What David Penkowoj says is correct but I want to add some more details to his answer.
The reason is that this is actually how programs gets its arguments, as a List of Strings. You can see this if you make this basic program:
void main(List<String> args) {
print(args);
}
We can then execute the program using a terminal:
>dart stackoverflow.dart first second third
[first, second, third]
As you can see, our terminal sends each argument into our program as a separate String value in our argument list.
But what if we want our arguments to be a single one? Well, in most terminals we can use " (see the comma between "second" and "third" is gone):
>dart stackoverflow.dart first "second third"
[first, second third]
But hey where did the " go? Well, this is just how our terminal (in my case CMD) does interpret my input and CMD uses " to signal if multiple arguments should be together even if separated with spaces.
If we want to send " in as arguments, we need to escape the " character so CMD knows it should see it as part of the argument:
>dart stackoverflow.dart first "\"second third\""
[first, "second third"]
So what does this has to do with Process.start? Well, the reason is that Process.start does not start your application though a terminal (e.g. CMD). Instead, it start the program directly and there are therefore not any interpretation of the arguments. Instead, the arguments are sent directly to the program being executed.
And as you can see, the standard behavior of a terminal (like CMD) does separate arguments separated with spaces. So if you want to start a program like you do from a terminal, you will also need to separate the arguments as individual String objects in a List<String>.
I believe this is due to the way programs parse command line arguments.
For humans, it is plausible that after an "option type" (-i) should come an "option value" (main). For computers it's not. They just parse the individual strings and decide how to use them, depending on how the programmer used the information.
It would be inconsistent, and inherently unsolvable (or at least too complicated) for the computer to be able to decide when to have arguments be split on a space or not.
I have the following spss syntax:
DO IF SYSMIS(V5).
COMPUTE V5 = LAG(V5).
END IF.
EXE.
It works fine. However I'd like to repeat the same process for several variables. I tried to write a macro to achieve this but I keep getting error messages. This was my attempt:
define filldown (!positional !cmdend).
do if sysmis(!1).
compute !1 = lag (!1).
end if.
execute.
!enddefine.
!filldown V5 age wt htm.
How do I write a macro that will work (I'm new to macros)?
#horace_vr's do repeat solution is definitely the right approach for this case.
The following is just to learn something about macros while you're at it.
First of all, you can use your present macro for each variable separately, but you need to use the original macro call (don't add "!"), so:
filldown V5.
filldown age.
....
But of course you can create a loop within the macro, like this:
define filldown (!positional !cmdend).
!do !vr !in (!1)
do if sysmis(!vr).
compute !vr = lag (!vr).
end if.
execute.
!doend
!enddefine.
Now you can use the macro call once with the complete list:
filldown V5 age wt htm.
The macro is simply a text substitution function. It will literally replace your !1 with whatever argument you are providing when calling the macro (V5 age wt htm).
To keep things simple, I would recommend using a simple do repeat command, instead of a macro, which may be little uncomfortable to use if you are not familiar with them
do repeat varlist=V5 age wt htm.
if sysmis(varlist) varlist=lag(varlist).
end repeat.
exe.
P.S.: If you really want to use your macro, you need to call it for each variable separately.
Set Auto sum To lisp. Other variables cannot be quantified. A quantifier is a lisp command which shows the next sum of the program.
Fill ! V2 you need to call V before rv,
V2 is positional, that is command is read only after changes has been made.
Define filldown ! v5
do! rv! fill! V2
..........
End
I would like to run the following syntax on lots of variables. Thus, I'd like to loop over a bunch of variables.
The syntax is the following:
compute v3a_mit = v3a.
recode v3a_mit
(-9998=2) (sysmis=9).
exe.
In this case, however, the syntax only concerns the variable "v3a".I have some other variables (v3b, v3c, v3d...) for which I would like to execute this syntax.
So, the loop should look like this.
DO REPEAT X=v3a to v3z
compute concat(X,'_mit') = X.
recode concat(X,'_mit')
(-9998=2) (sysmis=9).
exe.
END REPEAT.
So, within the loop, new variables shall be created which get a new name depending on the variable which is executed in the loop. The "SHIFT VALUES VARIABLE" command would be ideal (with shift=0) but this command cannot be used within a loop. Unfortunately "compute concat(X,'_mit')" does not work either.
CONCAT is a function for manipulating the values of string variables. So you can't use it for defining a variable name.
However you can make use of the !CONCAT function inside of a SPSS macro.
You can use the following macro to recode a set of variables.
DEFINE !recodeVars (vars = !CMDEND)
* for every variable in the 'vars' list do the RECODE INTO command.
!DO !var !IN (!vars)
RECODE !var (-9998=2) (sysmis=9) INTO !CONCAT(!var, '_mit').
!DOEND
!ENDDEFINE.
* macro call.
!recodeVars vars = v3a v3b v3c v3d.
Here, I used the RECODE INTO command, instead of one COMPUTE and a following RECODE command. But of course the principle of how to use the !CONCAT command would be the same for the COMPUTE operation.
However you can't call the macro in way like this !recodeVars vars = v3a TO v3z. In that case the macro would try perform the RECODE for the variables "v3a", "TO" and "v3z". You have to call this macro with the whole list of variables you want to recode.
This might be a lot of typing. As an easy way to avoid the typing, you could produce a SPSS command via the SPSS Menu (for example Analize -> Descreptive Statistics -> Frequencies) Then select the variables you want to recode (select the first variable, hold the SHIFT key and select the last variable) and then press the paste button. The Frequency command with the list of variables will be pasted to your syntax. You can now copy paste the variable list to your macro call.
If you have the Python integration plugin installed you could also use this small python block to retrieve the varlist between two variables.
BEGIN PROGRAM.
import spss,spssaux
variables = 'v3a to v3z' #Specify variables
varlist = spssaux.VariableDict().expand(variables)
spss.SetMacroValue('!varlist', ' '.join(varlist))
END PROGRAM.
This creates a macro named "!varlist" which expands to the list of variables when called.
You can now call the "!recodeVars" macro the following way: !recodeVars vars = !varlist.
If you don't have the python plugin installed (and don't want to use manual typing or copy and pasting) you can get the full variable list with the use of the "!DefList" macro from Raynald's SPSS Tools.
By the way, you can also make use of a macro for the SHIFT VALUES command.
[ set list N = 1 () set list N = 1
lput number-of-patches destination origin list N N + 1]
I wish to be able to store information about collections of patches and when the criteria for the filling of the list is met the number of the list will be increased. Will this code work?
Just looking at it, it will give you several syntax errors, regardless of whether the structure will do what you want. For example, the way you should construct a list with element '1' and name 'N' (which is what I think the first line is supposed to do) would be set N (list 1). You can test this by writing code as below and running test (eg by typing test in the command center at the bottom of the interface).
globals [N]
to test
set N (list 1)
print N
end
When writing code, your life is a lot easier if you build up the code in pieces, testing each one as you go either by inspecting agents to see if their property values change as you expect and/or putting print statements in lots of places to see what happens to your variables. This way you are introducing and fixing only a small number of errors in each step. Also, this means you are never writing code that you can't test immediately.
As documented, using run on a string runs into problems when local variables (including procedure parameters) are involved. So, what is the recommended way to achieve the following goal?
I have parameter tables (p1, p2, etc) specifying values for global variables, by name (i.e., table keys are strings, corresponding to the names of the global variables). Given a name (as a string) and a parameter table, I want to set the named global variable to the table value. E.g., if we were to use run, and if the values were all numbers, we might do something like this:
to update [#nm #tbl]
let %tval table:get #tbl #nm
run (word "set " #nm " " %tval)
end
What is the recommended way to do this, avoiding strings (due to the warning in the docs)?
As an additional complication, some table values may be tasks.
Extension of the question:
Following up on my coment of Oct 9, I found that if I isolate the assignment to a procedure, I can also successively make global assignments with tasks. E.g.,
to setGlobalTasks [#name #table]
;; #name : string, name of global variable
;; #table : table, maps names (strings) to values (reporter tasks)
let %tval table:get #table #name
run (word "set " #name " %tval")
end
Seth has provided some assurance that proceeding this way will continue to work in NetLogo when the assigned values are numbers. Is this going to be risky when the assigned values are tasks? Does it pose any risks in NetLogo 5.1?
Note: probably this extension of the question should be in the comments, but I could not format code blocks in a comment.
Your original approach, where %tval is outside of double quotes, only works with table values that are ordinary values like numbers, lists, or strings, values that can survive a round trip to string and back. (If you had trouble in practice, my guess would be that run got confused when you tried to combine it with foreach, as in the code you posted at http://groups.google.com/forum/#!topic/netlogo-devel/m5rnPEsxR44 . I believe this can be worked around by writing a standalone procedure like the one in your question.)
Your revised code, where %tval is inside the double quotes, and the whole thing is isolated in a separate procedure, is correct and should work for all possible table values. It should work fine in both NetLogo 5.0 and 5.1, and almost certainly in 6.0 too if there ever is a 6.0.
(In Tortoise, it wouldn't work, since run probably won't support strings at all in Tortoise.)
Tangent on reflection:
run on strings is kinda ugly. In situations where you want "reflective" setting of variables by name, where the name is stored in a string computed at runtime, it would be nice if there were an extension that supported this directly. The good news is, the code for the extension would be quite short and simple. The required methods within NetLogo, that the extension would call, already exist. The bad news is, writing a NetLogo extension of any kind is only easy if you're comfortable writing and compiling simple Java (or Scala) code. Anyway, it would be great if such a "reflection" extension existed. But in the meantime, you're safe with what you have.