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:
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 overexpected_types
, putting the keyword string into thetype
variable and the iteration count intoindex
.This is equivalent to using
for index = 0, n_elements(expected_types) - 1 do
and then usingexpected_types[index]
instead oftype
, but theforeach
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, likeforeach
vsfor
, 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 IDLexecute
function.