How can one give command line arguments to variables in a separate procedure using object references? - idl

IDL beginner here! Let's say I have two procedures, PRO1 and PRO2. If I receive a command line argument in PRO2, how can I give the argument value to a variable in PRO1?
I have previously tried to make an object reference ,'My', to PRO1, but I receive a syntax error on line 6.
PRO PRO2
opts = ob_new('mg_options)
opts.addOption, 'value', 'v'
opts.parseArgs,error_message = errorMsg
My = obj_new('PRO1')
My.A=opts.get('value')
END
For reference, I attempted to follow these instructions for receiving command line arguments: http://michaelgalloy.com/2009/05/11/command-line-options-for-your-idl-program.html

I had something else here, but I think your example above is actually what you want to avoid, yes? I'm not sure how it ends up being all that different, but if you want to make your procedure an object, you'll have to define an actual object (see here) and create methods for it containing your code functionality. Here's something close-ish.
In a file called pro1__define.pro:
function pro1::Init
self.A = 0L
return, 1
end
pro pro1::process, in_val
self.A = in_val
print, self.A
end
pro pro1__define
struct = {pro1, A:0L}
end
Then in pro2 you would do something like
arg = 2
pro1_obj = pro1()
pro1_obj->process, arg
Depending on which version of IDL you are using you may have to modify the initialization line to the obj_new() syntax.

Related

How do I get information about function calls from a Lua script?

I have a script written in Lua 5.1 that imports third-party module and calls some functions from it. I would like to get a list of function calls from a module with their arguments (when they are known before execution).
So, I need to write another script which takes the source code of my first script, parses it, and extracts information from its code.
Consider the minimal example.
I have the following module:
local mod = {}
function mod.foo(a, ...)
print(a, ...)
end
return mod
And the following driver code:
local M = require "mod"
M.foo('a', 1)
M.foo('b')
What is the better way to retrieve the data with the "use" occurrences of the M.foo function?
Ideally, I would like to get the information with the name of the function being called and the values of its arguments. From the example code above, it would be enough to get the mapping like this: {'foo': [('a', 1), ('b')]}.
I'm not sure if Lua has functions for reflection to retrieve this information. So probably I'll need to use one of the existing parsers for Lua to get the complete AST and find the function calls I'm interested in.
Any other suggestions?
If you can not modify the files, you can read the files into a strings then parse mod file and find all functions in it, then use that information to parse the target file for all uses of the mod library
functions = {}
for func in modFile:gmatch("function mod%.(%w+)") do
functions[func] = {}
end
for func, call in targetFile:gmatch("M%.(%w+)%(([^%)]+)%)") do
args = {}
for arg in string.gmatch(call, "([^,]+)") do
table.insert(args, arg)
end
table.insert(functions[func], args)
end
Resulting table can then be serialized
['foo'] = {{"'a'", " 1"}, {"'b'"}}
3 possible gotchas:
M is not a very unique name and could vary possibly match unintended function calls to another library.
This example does not handle if there is a function call made inside the arg list. e.g. myfunc(getStuff(), true)
The resulting table does not know the typing of the args so they are all save as strings representations.
If modifying the target file is an option you can create a wrapper around your required module
function log(mod)
local calls = {}
local wrapper = {
__index = function(_, k)
if mod[k] then
return function(...)
calls[k] = calls[k] or {}
table.insert(calls[k], {...})
return mod[k](...)
end
end
end,
}
return setmetatable({},wrapper), calls
end
then you use this function like so.
local M, calls = log(require("mod"))
M.foo('a', 1)
M.foo('b')
If your module is not just functions you would need to handle that in the wrapper, this wrapper assumes all indexes are a function.
after all your calls you can serialize the calls table to get the history of all the calls made. For the example code the table looks like
{
['foo'] = {{'a', 1}, {'b'}}
}

UndefVarError but variable is clearly defined

Suppose I have the following code which has two nested while loops.
struct Parameters
maxIter1::Float64
maxIter2::Float64
tolerance1::Float64
tolerance2::Float64
end
mutable struct Guess
x1::Float64
x2::Float64
end
function solveModel(par::Parameters,initGuess::Guess)
iterate1 = 0
error1 = 0
guess = initGuess
while (par.iterate1 < par.maxIter1 && error1 > par.tolerance1)
iterate1 += 1
iterate2 = 0
error2 = 0
guess.x2 = initGuess.x2
while (iterate2 < par.maxIter2 && error2 > par.tolerance2)
iterate2 += 1
z2 = solveInnerProblem(par,guess)
newGuess = update2(par,guess,z2)
error2 = computeError2(newGuess,guess)
guess = newGuess
end
guess = newGuess
end
end
I get an error message,
Note: the reference to the line number is erroenous - line 294 of my code contains no mention whatsoever of newGuess.
The error message goes away if I comment out the line
guess = newGuess
In the outer loop (last line before the final two end lines in the code snippet). I'm quite confused as to why this is happening. The variable newGuess is clearly defined, but Julia says it is not defined...
newGuess is a local variable, which means that it is defined in a localized part of the program rather than all the program. In the case of a local variable defined within a loop like a while statement, the variable is undefined outside the while loop within which it is defined, which is the inner while loop of your function. So the "not defined" error is because the program is trying to access the variable outside of its local scope-- it was defined before, but not when the error occurs.
You may need to define newGuess higher up, within the function, but before the inner while statement.

Julia: Append to an array

Someone please help me understand this. I have the following code below. I am trying to append index[i]-1 to an empty array. However I am getting this error: "BoundsError: attempt to access 0-element Array{Any,1} at index [1]" :
sample_size_array = [9,5,6,9,2,6,9]
n_minus_1 = []
array_length = length(sample_size_array)
for i in 1:array_length
n_minus_1[i].append(sample_size_array[i] -1)
end
println(n_minus_1)
If Julia does not understand array[0] then why is i starting at 0 and not at 1?
Your code has two problems:
in the first iteration you are trying to access n_minus_1 array at index 1 while this array is still empty (has 0 length) - this throws you an error;
in Julia you do not invoke methods using a . (this symbol is used for different purposes - in this case it is parsed as field access and also would throw an error later)
To solve both those problems use push! function which appends an element at the end of an array. The code could look like this:
sample_size_array = [9,5,6,9,2,6,9]
n_minus_1 = []
array_length = length(sample_size_array)
for i in 1:array_length
push!(n_minus_1, sample_size_array[i]-1)
end
println(n_minus_1)
However in this case the whole operation can be written even simpler as:
n_minus_1 = sample_size_array .- 1
and you do not need any loop (and here you see another use of . in Julia - in this case we use it to signal that we want to subtract 1 from every element of sample_size_array).

Julia: How to iterate with Channel

When I run the following code, I get a deprecation saying produce has been replace with channels.
function source(dir)
filelist = readdir(dir)
for filename in filelist
name,ext = splitext(filename)
if ext == ".jld"
produce(filename)
end
end
end
path = "somepathdirectoryhere"
for fname in Task(source(path))
println(fname)
end
I cannot find an example on how to do this with channels. I've tried creating a global channel and using put! instead of produce with no luck.
Any ideas?
Here's one way. Modify your function to accept a channel argument, and put! data in it:
function source(dir, chnl)
filelist = readdir(dir)
for filename in filelist
name, ext = splitext(filename)
if ext == ".jld"
put!(chnl, filename) % this blocks until "take!" is used elsewhere
end
end
end
Then create your task implicitly using the Channel constructor (which takes a function with a single argument only representing the channel, so we need to wrap the source function around an anonymous function):
my_channel = Channel( (channel_arg) -> source( pwd(), channel_arg) )
Then, either check the channel is still open (i.e. task hasn't finished) and if so take an argument:
julia> while isopen( my_channel)
take!( my_channel) |> println;
end
no.jld
yes.jld
or, use the channel itself as an iterator (iterating over Tasks is becoming deprecated, along with the produce / consume functionality)
julia> for i in my_channel
i |> println
end
no.jld
yes.jld
Alternatively you can use #schedule with bind etc as per the documentation, but it seems like the above is the most straightforward way.

idl: pass keyword dynamically to isa function to test structure read by read_csv

I am using IDL 8.4. I want to use isa() function to determine input type read by read_csv(). I want to use /number, /integer, /float and /string as some field I want to make sure float, other to be integer and other I don't care. I can do like this, but it is not very readable to human eye.
str = read_csv(filename, header=inheader)
; TODO check header
if not isa(str.(0), /integer) then stop
if not isa(str.(1), /number) then stop
if not isa(str.(2), /float) then stop
I am hoping I can do something like
expected_header = ['id', 'x', 'val']
expected_type = ['/integer', '/number', '/float']
str = read_csv(filename, header=inheader)
if not array_equal(strlowcase(inheader), expected_header) then stop
for i=0l,n_elements(expected_type) do
if not isa(str.(i), expected_type[i]) then stop
endfor
the above doesn't work, as '/integer' is taken literally and I guess isa() is looking for named structure. How can you do something similar?
Ideally I want to pick expected type based on header read from file, so that script still works as long as header specifies expected field.
EDIT:
my tentative solution is to write a wrapper for ISA(). Not very pretty, but does what I wanted... if there is cleaner solution , please let me know.
Also, read_csv is defined to return only one of long, long64, double and string, so I could write function to test with this limitation. but I just wanted to make it to work in general so that I can reuse them for other similar cases.
function isa_generic,var,typ
; calls isa() http://www.exelisvis.com/docs/ISA.html with keyword
; if 'n', test /number
; if 'i', test /integer
; if 'f', test /float
; if 's', test /string
if typ eq 'n' then return, isa(var, /number)
if typ eq 'i' then then return, isa(var, /integer)
if typ eq 'f' then then return, isa(var, /float)
if typ eq 's' then then return, isa(var, /string)
print, 'unexpected typename: ', typ
stop
end
IDL has some limited reflection abilities, which will do exactly what you want:
expected_types = ['integer', 'number', 'float']
expected_header = ['id', 'x', 'val']
str = read_csv(filename, header=inheader)
if ~array_equal(strlowcase(inheader), expected_header) then stop
foreach type, expected_types, index do begin
if ~isa(str.(index), _extra=create_struct(type, 1)) then stop
endforeach
It's debatable if this is really "easier to read" in your case, since there are only three cases to test. If there were 500 cases, it would be a lot cleaner than writing 500 slightly different lines.
This snipped used some rather esoteric IDL features, so let me explain what's happening a bit:
expected_types is just a list of (string) keyword names in the order they should be used.
The foreach part iterates over expected_types, putting the keyword string into the type variable and the iteration count into index.
This is equivalent to using for index = 0, n_elements(expected_types) - 1 do and then using expected_types[index] instead of type, but the foreach loop is easier to read IMHO. Reference here.
_extra is a special keyword that can pass a structure as if it were a set of keywords. Each of the structure's tags is interpreted as a keyword. Reference here.
The create_struct function takes one or more pairs of (string) tag names and (any type) values, then returns a structure with those tag names and values. Reference here.
Finally, I replaced not (bitwise not) with ~ (logical not). This step, like foreach vs for, is not necessary in this instance, but can avoid headache when debugging some types of code, where the distinction matters.
--
Reflective abilities like these can do an awful lot, and come in super handy. They're work-horses in other languages, but IDL programmers don't seem to use them as much. Here's a quick list of common reflective features I use in IDL, with links to the documentation for each:
create_struct - Create a structure from (string) tag names and values.
n_tags - Get the number of tags in a structure.
_extra, _strict_extra, and _ref_extra - Pass keywords by structure or reference.
call_function - Call a function by its (string) name.
call_procedure - Call a procedure by its (string) name.
call_method - Call a method (of an object) by its (string) name.
execute - Run complete IDL commands stored in a string.
Note: Be very careful using the execute function. It will blindly execute any IDL statement you (or a user, file, web form, etc.) feed it. Never ever feed untrusted or web user input to the IDL execute function.
You can't access the keywords quite like that, but there is a typename parameter to ISA that might be useful. This is untested, but should work:
expected_header = ['id', 'x', 'val']
expected_type = ['int', 'long', 'float']
str = read_cv(filename, header=inheader)
if not array_equal(strlowcase(inheader), expected_header) then stop
for i = 0L, n_elemented(expected_type) - 1L do begin
if not isa(str.(i), expected_type[i]) then stop
endfor

Resources