In SAS/IML is it possible to change a variable if only a macro with its name is available? Using symget on left side produces mistake:
proc iml;
variable = 0;
call symput ('macVar', 'variable');
/* &macVar = 1;*/
symget('macVar') = 1;
print variable;
quit;
ERROR 180-322: Statement is not valid or it is used out of proper order.
The &-sign works, but the code is in a do-loop and symget must be used.
The problem stems from task to write a function that accepts variable number of arguments and processes them in several do-loops. It is connected with the following questions:
SAS IML use of Mattrib with Macro (symget) in a loop
SAS IML pass reference to variable defined in macro
Loop over names in SAS-IML?
In other languages (R, C++, Java, Matlab, etc..) the task is solved with help of abstraction.
Ideas?
SOLVED
Thanks a lot.
Useful article.
Use the VALSET subroutine:
call valset(symget('macVar'), 1);
You find the article "Read hundreds of data sets into matrices." helpful, since so many of your questions are about similar.
Related
I'll start with my code:
macro example(args...)
local s = __source__
println(s) # This part works, showing macro is called on line 9
quote
println(s) # Julia tells me this variable "s" is not defined
println(__source__) # Likewise, "__source__" is not defined here either
end
end
#example 42 # Line 9 of my file
In my macro above I want to record the line number that is calling the macro and use it within my quote block. Both capturing it in a variable outside the quote block and using it within, or using it directly in the quote block don't work. My understanding is the code outside the quote block runs at parse-time, and the expression returned from the quote block is evaluated at run-time.
I feel like there must be a way to capture that variable and inject it right into the expression that will be evaluated later, but I haven't figured out how to do that. Any help here is appreciated. If there is a better way to do this let me know.
I ended up finding out an answer on my own. In the second line if I changed __source__ to __source__.line or __source__.file it worked fine as long as I then used $ to interpolate the result into the expression the macro returned. I'm still not sure why __source__ on its own didn't work, but using either .line or .file methods is working for me now.
I'm experiencing a similar problem trying to use __source__.
I think I can offer insight into why source.line, etc worked though.
The value of source.line is an integer. The value of source.fike is a string. Numbers and strings evaluate to themselves.
A symbol, on the other hand, evaluates to whatever value it has in the environment.
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'm experiencing a puzzling error in Matlab R2012b. It seems that variable names that are also data types exhibit strange behavior. Please see this small example:
function [] = test1()
dataset = 1;
if dataset ~= 0
disp hello
end
end
A call to test1() produces output hello, as expected.
Now, rather than set the value of dataset in my function, I run a script instead.
function [] = test2()
myscript;
if dataset ~= 0
disp hello
end
end
where myscript.m has one line:
dataset=1;
Now, when I call test2() I get this error:
Undefined function 'ne' for input arguments of type 'dataset'.
Error in test2 (line 4)
if dataset ~= 0
(Forgive the variable named dataset - I know that it is also the name of a data type, and it came in the code I was running.) So it seems as if in test2, Matlab creates an empty dataset object rather than using the variable named dataset. Furthermore, this behavior only appears when I set the value in a script rather than in the function body. Even more weird, is that I can do:
>> dbstop in test2 at 4 % line of if statement
>> test2()
K>> dataset
dataset =
1.00
K>> dataset ~= 0
ans =
1
K>> if dataset ~= 0, disp hello; end
hello
K>> dbcont
and I get the same error! The error is not displayed in debugging mode but it is in normal execution.
Can anyone reproduce this? What is going on here?
The MATLAB online help has some pages dealing with this issue; Variables Names and Loading Variables within a Function seem to be the most relevant.
There is no explicit page that discusses how MATLAB resolves names at compilation time, but there is one little tidbit at the bottom of the Variables Names page: "In some cases, load or eval add variables that have the same names as functions. Unless these variables are in the function workspace before the call to load or eval, the MATLAB parser interprets the variable names as function names."
In other words, if the parser finds an explicit assignment to a variable whose name is the same as another existent object, the local definition takes precedence.
In your test2(), there is no explicit assignment to a variable dataset; therefore, when the file is compiled, the parser interprets dataset to be a class constructor (since the parser will not run or inline myscript into the function).
Then at run-time, even though a variable named dataset has been poofed1 into the function's workspace, the interpreted code that is running still has the dataset symbol in the if-statement associated with the class constructor.
If you need to, you can still use the dataset variable name and load from an external file, but it should be done with an explicit assignment via a function call. For example:
dataset = initialize();
Now the parser will notice that dataset is some arbitrary output of the function initialize and all will be well. In fact, you can have even have initialize return a dataset constructor to the dataset variable if you wanted.
1 When variables are defined without explicit assignment, MATLAB people (at least on some of their blogs I've read) called this 'poofing'. Using load without any output arguments, using eval, and simply running scripts (not functions) can all poof variables into the workspace. This can work fine as long as the variable names do not conflict with other in-use symbols at compile time.
I'm using RRDs::Simple function and it needs bunch of parameters.
I have placed these parameters in a special variable (parsing, sorting and calculating data from a file) with all quotes, commas and other stuff.
Of course
RRDs::create ($variable);
doesn't work.
I've glanced through all perl special variables and have found nothing.
How to substitute name of variable for the data that contained in that variable?
At least could you tell me with what kind of tools(maybe another special variables) it can be done?
Assuming I'm understanding what you're asking:
You've build the 'create' data in $variable, and are now trying to use RRDs::create to actually do it?
First step is:
print $variable,"\n"; - to see what is actually there. You should be able to use this from the command line, with rrdtool create. (Which needs a filename, timestep, and some valid DS and RRA parameters)
usually, I'll use an array to pass into RRDs::create:
RRDs::create ( "test.rrd", "-s 300",
"DS:name:GAUGE:600:U:U", )
etc.
If $variable contains this information already, then that should be ok. The way to tell what went wrong is:
if ( RRDs::error ) { print RRDs::error,"\n"; }
It's possible that creating the file is the problem, or that your RRD definitions are invalid for some reason. rrdtool create on command line will tell you, as will RRDs::error;
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.