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
Related
I am playing around with Elixir macros - specifically macros that call themselves which is something that I do often in Scheme. I have created a little test macro below however it just hangs iex - nothing is printed to console. Does anyone have any insight into why and what could be done to correct it?
defmodule RecMac do
defmacro test_rec(x) do
quote do
IO.puts("Started?")
if(unquote(x) < 1) do
IO.puts("Done?")
"done"
else
IO.puts("Where are we")
IO.puts(unquote(x))
RecMac.test_rec(unquote(x) - 1)
end
end
end
end
EDIT!!
OK, so it turns out you can define recursive macros where there is a structural difference to match on (eg lists). The following is working for me. And to confirm #Aleksei Matiushkin below, the above will not work and does indeed not work in scheme!
defmacro test_rec([h | t]) do
quote do
IO.inspect([unquote(h) | unquote(t)])
RecMac.test_rec(unquote(t))
end
end
defmacro test_rec([]) do
quote do
IO.puts "Done"
end
end
end
I am very happy to have dug into this as I learned something about two languages!
TL;DR: this is impossible.
Macros in elixir are not what you expect them to be. When a compiler sees a macro, it calls it during a compilation time and injects the AST it returned in the place of where it was called. That said, recursive macros would always lead to the infinite loop at the compilation stage.
Put IO.puts("something") before quote do instruction and you’ll see it to be printed infinitely, once per subsequent call to expand the macro.
You can use #compile {:inline, test_rec: 1} to achieve the behaviour you are after. For better understanding macros, you probably should read Macros section in the Elixir Guide in general and this excerpt in particular:
Macros are harder to write than ordinary Elixir functions and it’s considered to be bad style to use them when they’re not necessary. So write macros responsibly.
Actually you can do kind of recursion, but point is to think what you are doing to avoid calling macro inside quote do [....] end. Your example with IO.puts would be something like this
defmodule RecMac do
defmacro test_rec(list) do
{:__block__, [], quote_value(list)}
end
defp quote_value([]), do: []
defp quote_value([h | t]) do
if h < 1 do
[]
else
ast = quote do
IO.puts("#{unquote(h)}")
end
[ast | quote_value(t)]
end
end
end
you will notice two things, I do use recursion in macro but outside quote using private function quote_value/1 and that function has logic that "stops" after it finds value lower than 1. All "quotes" are put into list and trick is to put this list into tuple {:__block__, [], put_quote_list_here}
Now note that this macro wont compile if list parameter of test_rec is not know upfront (during compile time), so you need to call macro test_rec(["a", "b", 0, 100, 200]) so compiler knows size and elements of that list.
BTW, I used body optimized recursion, but you can easily add accumulator and convert this into tail optimized recursion.
I am trying to write a macro to perform the following operation:
do if SYSMIS(V2).
compute V3=V1.
ELSE.
compute V3=V2.
end if.
I tried several approaches, but it always gets stuck on the compute command.
One important thing, you should know about SPSS macros is, that the SPSS macro language is just a "string parser". This means, the text within a DEFINE !ENDDEFINE block is parsed and as an output a syntax code will be created. The variables (beginning with "!") will be substituted by the strings(values) assigned to these variables.
So the line COMPUTE !var1 = !var2. will produce a compute command, with the variables names assigned to !var1 and !var2.
On the other hand a command like !IF (SYSMIS(!Var2)) !Var1 = !Var3.
makes no sense, because what you actually want is to execute an IF command, while !IF is a conditional macro directive, which 'decides' if some piece of syntax code be generated by the macro (if the condition is met), or not.
So you solution would look something like this:
DEFINE !crevar ( !POSITIONAL !TOKENS(1)
/!POSITIONAL !TOKENS(1)
/!POSITIONAL !TOKENS(1))
DO IF SYSMIS(!2).
compute !3=!1.
ELSE.
compute !3=!2.
END IF.
!ENDDEFINE.
* Macro call.
!crevar V1 V2 V3.
EXECUTE.
The macro call above, will be evaluated by the macro parser to the code you have given in your question.
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.
Suppose I have this macro definition in a module:
module Example
export #example_macro
macro example_macro(a)
quote
local r = RemoteRef()
put!(r, $(esc(a)))
remotecall_fetch(2, (r) -> fetch(r), r)
end
end
end
And here is its expansion:
julia> include("Example.jl")
julia> using Example
julia> macroexpand(quote #example_macro a end)
quote # none, line 1:
begin # /.../Example.jl, line 7:
local #121#r = Example.RemoteRef() # line 8:
Example.put!(#121#r,a) # line 9:
Example.remotecall_fetch(2,(r) -> Example.fetch(r),#121#r)
end
end
Every single one of globally available functions (like put! or fetch) are prefixed with the name of the module. I understand that this is needed for the macro to be hygienic - if, say, fetch was redefined in the module in which #example_macro is called, and fetch was inserted into the expansion as is, it wouldn't work correctly.
However, this also requires Example module to be available not only in the main process, but also on the second worker (since remotecall_fetch needs to execute Example.fetch on it). I don't want it - after all, fetch is a basic function available on all workers by default.
So, is there a way to disable prefixing all identifiers with the name of the current module? I think this would mean turning the macro non-hygienic as it is impossible to decide where some identifier (like fetch) is defined on macro expansion phase, and that's fine for me.
Since this is a pretty profound question, I think that you should give the Julia devs themselves a chance to answer it by asking on julia-users.
Currently, you can completely circumvent macro hygiene by wrapping the whole quote block in your macro in an esc(...) (don't forget to take away the esc around a), but I would in general advise against it - then you are on your own.
I have a rather bulky program that I've been running as a script from the MATLAB command line. I decided to clean it up a bit with some nested functions (I need to keep everything in one file), but in order for that to work it required me to also make the program itself a function. As a result, the program no longer runs in the base workspace like it did when it was a script. This means I no longer have access to the dozens of useful variables that used to remain after the program runs, which are important for extra calculations and information about the run.
The suggested workarounds I can find are to use assignin, evalin, define the variables as global, or set the output in the definition of the now function-ized program. None of these solutions appeal to me, however, and I would really like to find a way to force the workspace itself to base. Does any such workaround exist? Or is there any other way to do this that doesn't require me to manually define or label each specific variable I want to get out of the function?
Functions should define clearly input and output variables. Organizing the code differently will be much more difficult to understand and to modify later on. In the end, it will most likely cost you more time to work with an unorthodox style than investing in some restructuring.
If you have a huge number of output variables, I would suggest organizing them in structure arrays, which might be easy to handle as output variables.
The only untidy workaround I can imagine would use whos, assignin and eval:
function your_function()
x = 'hello' ;
y = 'world' ;
variables = whos ;
for k=1:length(variables)
assignin('base',variables(k).name,eval(variables(k).name))
end
end
But I doubt that this will help with the aim to clean up your program. As mentioned above I suggest ordering things manually in structures:
function out = your_function()
x = 'hello' ;
y = 'world' ;
out.x = x ;
out.y = y ;
end
If the function you would like to define are simple and have a single output, one option is to use anonymous functions.
Another option is to store all the variable you would like to use afterwards in a struct and have your big function return this struct as an output.
function AllVariables = GlobalFunction(varargin);
% bunch of stuff
AllVariables= struct('Variable1', Variable1, 'Variable2', Variable2, …);
end