I am new to netlogo and I am having trouble understanding the performance issues related to using run with anonymous procedures.
My main concern is whether using run or runresult with an anonymous procedure incurs performance penalties or if this is something related to using run/runresult with string input only.
Thanks
Some code :
My understanding is that you can achieve the same result with the following two different ways (there might be more but like I said I'm just starting with netlogo so these are the ones I can come up with):
to-report list-made-up-of-empty-lists? [ a-list ]
let helper [ [ arg ] -> ( reduce and ( map empty? arg ) )]
report ( map helper ( list a-list ) )
end
vs
to-report list-made-up-of-empty-lists? [ a-list ]
let helper [ [ arg ] -> ( reduce and ( map empty? arg ) )]
report ( list ( runresult helper a-list ) )
end
What's the difference?
Also, in the documentation it is stated that something like runresult ( word "helper " a-list ) should work in principle but I can't make it run (I get runtime errors).
Shouldn't this last line evaluate correctly? What am I doing wrong? Also, in what sense can runresult 'run' strings?
Executing an anonymous procedure is significantly quicker than running a string. The issue is that the string needs to be converted to some executable code, then executed.
If you're comparing user defined functions vs anonymous procedures, that's a different story and dependent on use case. For example, if you have a for loop and create an anonymous function inside the loop, rather than creating it once outside the loop (or pre-defining it), you'd potentially start to see a slow down.
Netlogo's Run documentation:
run command
(run command input1 ...)
run string
runresult reporter
(runresult reporter input1 ...)
runresult string
The run form expects the name of a command, an anonymous command, or a string containing commands. This agent then runs them.
The runresult form expects the name of a reporter, an anonymous reporter, or a string containing a reporter. This agent runs it and reports the result.
Furthermore, the reason why your code won't work with a string is due to the following: Netlogo's string commands can't set/read local variables. Your helper is a local variable.
See the documentation:
Anonymous procedures may freely read and/or set local variables and
procedure inputs. Trying to do the same with strings may or may not
work and should not be relied on.
Related
I need to run SPSS syntax in an IF statement, which tests if a variable exists in the document. I am having trouble getting the IF test right. I'm trying to do this:
do if (test if myVariable exists).
// execute code here
end if.
Execute.
I've looked here and tried this:
DO IF (myVariable) exist=1.
END IF.
Execute.
But I get the error 'There is extraneous text following the logical expression on a DO IF command. Have I misunderstood the code?
spssinc select variables command creates a list of variables according to a specified propertie. In this case the property will be the variable called "MyVar". If the variable doesn't exist, the list will stay empty:
spssinc select variables macroname="!findMyVar" /properties pattern="MyVar".
Now we define a macro that will run some commands only if the above list is not empty:
define doifMyVarexists ()
!if (!eval(!findMyVar)<>"") !then
* put your commands here, like following examples.
compute MyVar=MyVar*2.
freq MyVar.
!ifend
!enddefine.
* the macro is ready, now we call it:
doifMyVarexists.
If you run this multiple times, you will face a problem that if MyVar exists once and in a later run doesn't exist - the list doesn't get emptied (it is only overwritten if there were variables to put into it).
To solve that use the following line to empty the list before running select variables again:
define !findMyVar() !enddefine.
I would like to conditionally process blocks of syntax where the condition is based on the active data set.
Within an SPSS macro, you can conditionally process a block of syntax using the !IF/!IFEND macro command. However, as far as I can tell, the user is required to explicitly give a value to the flag by either using the !LET command (!LET !FLAG = 1), or by using a Macro input variable. This is wildly different from my experience with other languages, where I can write code that has branching logic based on the data I'm working with.
Say that there is a block of syntax that I only want to run if there are at least 2 records in the active data set. I can create a variable in the data set which is equal to the number of records using the AGGREGATE function, but I can't find a way to make a macro variable equal to that value in a way that is usable as a !IF condition. Below is a very simple version of what I'd like to do.
COMPUTE DUMMY=1.
AGGREGATE
/OUTFILE = * MODE = ADDVARIABLES
/BREAK DUMMY
/NUMBER_OF_CASES = N.
!LET !N_CASES = NUMBER_OF_CASES.
!IF (!N_CASES > 1) !THEN
MEANS TABLES = VAR1 VAR2 VAR3.
!IFEND
Is what I'm attempting possible? Thanks in advance for your time and consideration.
Following is a way to put a value from the dataset into a macro, which you can then use wherever you need - including in another macro.
First we'll make a little dataset to recreate your example:
data list free/var1 var2 var3.
begin data
1 1 1 2 2 2 3 3 3
end data.
* this will create the number of cases value:
AGGREGATE /OUTFILE = * MODE = ADDVARIABLES /BREAK /NUMBER_OF_CASES = N.
Now we can send the value into a macro - by writing a separate syntax file with the macro definition.
do if $casenum=1.
write out='SomePath\N_CASES.sps' /"define !N_CASES() ", NUMBER_OF_CASES, " !enddefine.".
end if.
exe.
insert file='SomePath\N_CASES.sps'.
The macro is now defined and you can use the value in calculations (e.g if you want to use it for analysis of a different dataset, or later in your syntax when the current data is not available).
for example:
compute just_checking= !N_CASES .
You can also use it in your macro as in your example - you'll see that the new macro can't read the !N_CASES macro as is, that's why you need the !eval() function:
define !cond_means ()
!IF (!eval(!N_CASES) > 1) !THEN
MEANS TABLES = VAR1 VAR2 VAR3.
!IFEND
!enddefine.
Now running the macro will produce nothing if there is just one line in your data, and will run means if there was more than one line:
!cond_means.
A table is being opened in a folder whose name is being provided by the user.
lFolder = Getfile()
lFilename = lFolder + “mytable.dbf”
USE &lFilename IN 0 ALIAS . . .
This usually works fine. However, if the folder whose name is supplied by the user has an embedded space, so ‘My folder’, the USE instruction fails. But this instruction works successfully :
USE (lFilename) IN 0 . . .
Are there any rules which say when one should use the Ampersand (&) construct and when one should use the bracket construct? And is this only applicable to the USE statement?
Thanks. Andrew
The proper way to write that code is:
local lFolder, lFilename
lFolder = Getdir()
lFilename = addbs(m.lFolder) + 'mytable.dbf'
* or a single GetFile() to select the dbf directly
USE (m.lFilename) IN 0 ALIAS . . .
There are more than one point in this code:
1) Declare your variables as local. Without that declaration, it would work and VFP would implicitly declare them as private. It is a good practice to declare local and also would help with intellisense, if you use tools like ISX.
2) Using addbs() ensures a backslash. It is just coding safe.
3) Use m. (aka mdot) for memory variables. Using mdot, you are telling VFP explicitly that it is a memory variable. Using mdot there is no harm, but if you don't you might get into hard to catch bugs (and also in tight loops, it is proven to be much faster using mdot).
4) Finally, your original question. A Filename is a "name" so do not use a macro expansion (&) operator but "name expression" anywhere there is a Name. A "name expression" is simply a set of parentheses. If something is a "name", then use "name expression" (a fieldName, fileName, folderName, variableName ...).
Apart from rules, unfortunately many VFP developers abuse the & and use it too often. In reality, probably it has too few places where using makes sense and that is SQL clauses. Not something like:
lcSQL = "select * from ..." + ... + "..."
&lcSQL
(which often you may see this pattern as well) but this one where parts of SQL use macro expansion. ie:
select &fieldList ;
from (m.tableName) ;
where &lcWhere ;
order by &lcOrder
Note that m.tableName is a "name" and thus used with "name expression". FieldList variable might be holding a single fieldName or a series of fieldNames (ie: "CustomerId" or "CustomerId, CompanyName, ContactName") and cannot be used as a "name expression", needs to be macro expanded.
I have a Julia function that takes a few input arguments and uses them to perform multiple operations. In order to get one of those operations to work properly I need to be able to compute a symbol that matches the user input. For example the function looks something like this:
function func(arg1,arg2)
symb_arg1 = ## get the symbol for input into arg1
symb_arg2 = ## get the symbol for input into arg2
println(symb_arg1)
println(symb_arg2)
## Do some operations using arg1, arg2, symb_arg1, symb_arg1
end
I am hoping to achieve the following behavior:
a = 25
b = rand(27,55,18)
func(a,b) ## prints :a and :b
The difficulty here is to get the function to compute a symbol containing the actual name of the variable, rather than the value of the variable. This post provides the following macro that almost does what I want:
macro mymacro(ex)
Expr(:quote,ex) # this creates an expression that looks like :(:(x + 2))
end
This macro works well for doing the following:
a = rand(27,15)
symb_a = #mymacro(a) ## prints :a
However, using this macro inside my function will not produce the desired effect. Specifically, if I define my function as:
function func_bad(arg1,arg2)
symb_arg1 = #mymacro(arg1)
symb_arg2 = #mymacto(arg2)
println(symb_arg1)
println(symb_arg2)
## Do some operations using arg1, arg2, symb_arg1, symb_arg1
end
And then run the commands:
a = 25
b = rand(27,55,18)
func_bad(a,b) ## prints :arg1 and :arg2 (instead of the desired :a and :b)
Of course, one simple (but not so elegant) solution is to add additional input arguments for the function so that the user is responsible for creating the symbols. However this is more of a last resort. I would prefer the function be able to automatically create the symbols. Any idea as to how I can modify my function or the macro to achieve this behavior?
The simple answer is that this is not possible; there is an abstraction barrier that prevents functions from seeing implementation details of their callers. All they get is the values, which is a crucial property for robust and clear programs.
One thing you could do is write a macro to transform the call site:
#with_syms func(a, b)
into something like
func((:a,a), (:b,b))
passing symbol-value pairs into the function.
Another slightly different design would be to provide macro wrappers for all functions that need this behavior, so that calls would look like #func(a,b). You could factor out the argument list transformation to a helper function; each macro would look like
macro func(args...)
:(func($(pair_with_symbols(args)...)))
end
I don't think SPSS macros can return values, so instead of assigning a value like VIXL3 = !getLastAvail target=VIX level=3 I figured I need to do something like this:
/* computes last available entry of target at given level */
define !compLastAvail(name !Tokens(1) /target !Tokens(1) /level !Tokens(1))
compute tmpid= $casenum.
dataset copy tmpset1.
select if not miss(!target).
compute !name= lag(!target, !level).
match files /file= * /file= tmpset1 /by tmpid.
exec.
delete variables tmpid.
dataset close tmpset1.
!enddefine.
/* compute last values */
!compLastAvail name="VIXCL3" target=VIXC level=3.
The compute !name = ...is where the problem is.
How should this be done properly? The above returns:
>Error # 4285 in column 9. Text: VIXCL3
>Incorrect variable name: either the name is more than 64 characters, or it is
>not defined by a previous command.
>Execution of this command stops.
When you pass tokens to the macro, they get interpreted literally. So when you specify
!compLastAvail name="VIXCL3"
It gets passed to the corresponding compute statement as "VIXCL3", instead of just a variable name without quotation marks (e.g. VIXCL3).
Two other general pieces of advice;
If you do the command set mprint on before you execute your macro, you will see how your tokens are passed to the macro. In this instance, if you had taken that step, you would have seen that the offending compute statement and error message.
Sometimes you do what to use quotation marks in tokens, and when that is the case the string commands !QUOTE and !UNQUOTE come in handy.