highsciguy
highsciguy

Reputation: 2647

Conditionals for data type in gnuplot functions

I would like to define a function which returns the string "NaN" or sprintf("%g",val) depending on whether val is a string or a numeric value. Initially I was trying to test if val was defined (using the gnuplot "exists" function) but it seems that I cannot pass any undefined variable to a function (an error is issued before the function is evaluated). Therefore: is there a way to test inside a function whether the argument is a string or numeric?

I search for a function isstring which I can use somehow like

myfunc(val)=(isstring(val)?"NaN":sprintf("%g",val))

The goal is to output the values of variables without risking errors in case they are undefined. However I need it as a function if I want a compact code for many variables.

Upvotes: 1

Views: 2408

Answers (2)

CodeMaster
CodeMaster

Reputation: 11

You helped me a lot these days with gnuplot. I want to give you something back because I have found a solution to check if a variable is numeric or not. This helps to decide which operators can be used on it (e.g. == for numbers, eq for strings).

The solution is not very simple, but it works. It redirects gnuplot's print command to a temp file, writes the variable to the file with print myvar and evaluates the file's first line with system("perl -e '<isnumeric(line#1 in temp file)?>' ") (<> is pseudo-code). Let me know if there's room for imrpovements and let me hear your suggestions!

Example: myvar is a float. Any integer (1 or "1") or string value ("*") works too!

myvar = -2.555  
 # create temporary file for checking if variables are numeric  
Int_tmpfle = "tmp_isnumeric_check"  
 # redirect print output into temp file (existing file is overwritten)  
set print Int_tmpfle    
 # save variable's value to file  
print myvar             
 # check if file is numeric with Perl's 'looks_like_number' function   
isnumeric = system("perl -e 'use Scalar::Util qw(looks_like_number); \  
open(FLE,".Int_tmpfle."); $line = < FLE >; \  
if (looks_like_number($line) > 0) {print qq(y)} ' ")  
 # reset print output to < STDOUT> (terminal)  
set print "-"           
 # make sure to use "," when printing string and numeric values  
if (isnumeric eq "y") {print myvar," is numeric."} else {print myvar," is not numeric."}

Upvotes: 1

mgilson
mgilson

Reputation: 309969

Gnuplot doesn't really have the introspection abilities that many other languages have. In fact, it treats strings and numbers (at least integers) very similarly:

print "1"+2  #prints 3
a=1
print "foo".a  #prints foo1

I'm not exactly sure how this is implemented internally. However, what you're asking is very tricky to get to work.

Actually, I think your first attempt (checking if a variable exists) is more sensible as type-checking in gnuplot is impossible*. You can pass the variable name to the function as a string, but the problem is that you don't seem to have a handle on the value. All seems lost -- But wait, gnuplot has an eval statement which when given a string will evaluate it. This seems great! Unfortunately, it's a statement, not a function (so it can't be used in a function -- argv!). The best solution I can come up with is to write a function which returns an expression that can be evaluated using eval. Here goes:

def exists_func(result,var)=sprintf("%s=exists('%s')?sprintf('%g',var):'NaN'",result,var,var)

Now when you want to use it, you just prefix it with eval

a=3
eval exists_func("my_true_result","a")
print my_true_result #3
eval exists_func("my_false_result","b")
print my_false_result #NaN

This goes against the grain a little bit. In most programming languages, you'd probably want to do something like this:

my_true_result=exists_func(a)

But alas, I can't figure out how to make that form work.

Of course, the same thing goes here that always goes with eval. Don't use this function with untrusted strings.

*I don't actually know that it's impossible, but I've never been able to get it to work

EDIT

In response to your comment above on the question, I think a function like this would be a little more intuitive:

def fmt(x)=(x==x)?sprintf("%g",x):"NaN"

With this function, your "sentinal/default" value should be NaN instead of "undefined", but it doesn't seem like this should make too much of a difference...(Really, if you're willing to live with "nan" instead of "NaN" you don't need this function at all -- sprintf will do just fine. (Note that this works because according to IEEE, NaN doesn't equal anything (even itself)).

Upvotes: 2

Related Questions