wave assignment and multiple return statements
ChrLie
I wonder if there is some way to assign the elements of two waves simultaneously with one function, something like the following (silly) MWE:
function [variable out1, variable out2] sub(variable in)
out1 = in/2
out2 = in*2
return [out1, out2]
end
function main()
make/O/N=(2,2) W_in=p*q+1, W_out1, W_out1
[W_out1[][], W_out2[][]] = sub(W_in[p][q])
end
out1 = in/2
out2 = in*2
return [out1, out2]
end
function main()
make/O/N=(2,2) W_in=p*q+1, W_out1, W_out1
[W_out1[][], W_out2[][]] = sub(W_in[p][q])
end
Main() doesn't compile. The alternative is to do an explicit loop in Main(), or to split sub() into to two separate functions. Neither seems attractive. The actual use-case here is to split a string expression to return two values.
In case the two returns is not possible, with what you want, would it work to put the return the two strings in a 1x2 text wave function?
W_out1[][] = splittextwave(w_in[p])[q]
October 3, 2024 at 09:23 am - Permalink
out1 = in/2
out2 = in*2
return [out1, out2]
end
function main()
make/O/N=(2,2) W_in=p*q+1, W_out1, W_out2
[W_out1, W_out2] = sub(W_in)
end
I haven't thought about any performance consequences of this, but if you change to inputting and returning waves instead of variables, it will compile and I think do what you want.
October 3, 2024 at 09:38 am - Permalink
JJ, I had considered something like that, to return a one point, two layer wave, but I didn't get this to work.
Ben, the wave approach will not work in my case, which is given below. I want to extract data and error values from string expressions in which the error is reported in terms of last significant digit. The strings are copied & pasted into a 2D text wave. The main function works, I just hoped I could get rid of the nested loops, mainly for aesthetic reasons.
variable nRows=DimSize(txt2d,0)
variable nCols=DimSize(txt2d,1)
Make/D/O/N=(nRows, nCols), W_values, W_errors
CopyDimlabels txt2D, W_values, W_errors
variable i, k, value, error
for(i=0; i<nRows; i++)
for(k=0; k<nCols; k++)
[value, error] = SplitExpression(txt2d[i][k])
W_values[i][k] = value
W_errors[i][k] = error
endfor
endfor
end
function [variable value, variable error] SplitExpression(string str)
// split the input string, such as e.g. "3.25(4)", into two numeric parts
// ignore any other non-numeric characters that may be in the string, as in e.g. "3.25 (4)#"
string sValue, sError
splitstring/E=".*?([0-9.]+).*?\(\s*(\d+)\s*\).*" str, sValue, sError
// does value has a decimal point, if yes how many digits behind it?
variable PosDecimalPoint = strsearch(sValue, ".", 0)
variable nDigits = PosDecimalPoint == -1 ? NaN : (strlen(sValue) - PosDecimalPoint -1)
value = str2Num(sValue)
if (PosDecimalPoint == -1)
// if value has no decimal point, error is absolut
error = str2num(sError)
else
// if yes, scale error according to digits after decimal of data value
error = str2num(sError)/10^(nDigits)
endif
return [value, error]
end
// sample data:
Make/O/T txt = {{"23652 (23)"," 0.125(3)"}, {"125.5(9)","2.3(11)a)"}}
October 4, 2024 at 12:42 am - Permalink
ok, I think I do it like this, but thanks for the comments!
variable nRows=DimSize(txt2d,0)
variable nCols=DimSize(txt2d,1)
Make/D/O/N=(nRows, nCols), W_values, W_errors
CopyDimlabels txt2D, W_values, W_errors
W_values = SplitExpression(txt2D[p][q], 0)
W_errors = SplitExpression(txt2D[p][q], 1)
end
function SplitExpression(string str, int mode)
// split the input string, such as e.g. "3.25(4)", into two numeric parts
// ignore any other non-numeric characters that may be in the string, as in e.g. "3.25 (4)#"
variable Value, Error
string sValue, sError
splitstring/E=".*?([0-9.]+).*?\(\s*(\d+)\s*\).*" str, sValue, sError
// does value has a decimal point, if yes how many digits behind it?
variable PosDecimalPoint = strsearch(sValue, ".", 0)
variable nDigits = PosDecimalPoint == -1 ? NaN : (strlen(sValue) - PosDecimalPoint -1)
value = str2Num(sValue)
if (PosDecimalPoint == -1)
// if value has no decimal point, error is absolut
error = str2num(sError)
else
// if yes, scale error according to digits after decimal of data value
error = str2num(sError)/10^(nDigits)
endif
variable out=nan
out = (mode ? error : value)
return out
end
October 4, 2024 at 12:58 am - Permalink
Seems like overhead to go through the split expression function twice.
variable nRows=DimSize(txt2d,0)
// variable nCols=DimSize(txt2d,1)
Make/D/O/N=(nRows, 2), W_values, W_errors
CopyDimlabels txt2D, W_values, W_errors
Make/D/O/N=(nRows,2)/FREE rvalues
rvalues[][]= SplitExpression(txt2D[p][q],0)[q]
W_values = rvalues[p][0]
W_errors = rvalues[p][1]
return 0
end
function/WAVE SplitExpression(string str, int mode)
// split the input string, such as e.g. "3.25(4)", into two numeric parts
// ignore any other non-numeric characters that may be in the string, as in e.g. "3.25 (4)#"
variable Value, Error
string sValue, sError
splitstring/E=".*?([0-9.]+).*?\(\s*(\d+)\s*\).*" str, sValue, sError
// does value has a decimal point, if yes how many digits behind it?
variable PosDecimalPoint = strsearch(sValue, ".", 0)
variable nDigits = PosDecimalPoint == -1 ? NaN : (strlen(sValue) - PosDecimalPoint -1)
value = str2Num(sValue)
if (PosDecimalPoint == -1)
// if value has no decimal point, error is absolut
error = str2num(sError)
else
// if yes, scale error according to digits after decimal of data value
error = str2num(sError)/10^(nDigits)
endif
make/N=(2)/D/FREE rwave
rwave[0] = value
rwave[1] = error
return rwave
end
October 5, 2024 at 09:06 am - Permalink
JJ, thanks, but I think your version will not work for the general case of a 2D text wave as input, at least it doesn't for the example data in my second post. Nonetheless, it inspired my to go back to the earlier thought to return a one point two layer wave. I initially didn't get this to work, because I couldn't fit the r-index into the function call, and it just didn't occur to me at the time that it must be outside (as in your example with [q]), but yes, of course!
Here the 3D return wave version:
variable nRows=DimSize(txt2d,0)
variable nCols=DimSize(txt2d,1)
Make/D/O/N=(nRows,nCols,2)/FREE rvalues
rvalues = SplitExpression(txt2D[p][q])[r]
SplitWave/O/NAME="W_values;W_errors" rvalues
return 0
end
function/WAVE SplitExpression(string str)
// split the input string, such as e.g. "3.25(4)", into two numeric parts
// ignore any other non-numeric characters that may be in the string, as in e.g. "3.25 (4)#"
variable Value, Error
string sValue, sError
splitstring/E=".*?([0-9.]+).*?\(\s*(\d+)\s*\).*" str, sValue, sError
// does value has a decimal point, if yes how many digits behind it?
variable PosDecimalPoint = strsearch(sValue, ".", 0)
variable nDigits = PosDecimalPoint == -1 ? NaN : (strlen(sValue) - PosDecimalPoint -1)
value = str2Num(sValue)
if (PosDecimalPoint == -1)
// if value has no decimal point, error is absolut
error = str2num(sError)
else
// if yes, scale error according to digits after decimal of data value
error = str2num(sError)/10^(nDigits)
endif
make/N=(1,1,2)/D/FREE rwave
rwave[0][0][0] = value
rwave[0][0][1] = error
return rwave
end
October 7, 2024 at 01:35 am - Permalink
In reply to Seems like overhead to go… by jjweimer
the implicit loop also runs SplitExpression twice for each string.
October 7, 2024 at 01:05 pm - Permalink
Tony -- I wonder about the administrative overhead of what I see as (loading + running + unloading)x2 (as per Chris' original) versus (loading + (running)x2 + unloading). Both run twice, but the second option loads/unloads once (as far as I understand the implicit behavior).
October 7, 2024 at 01:36 pm - Permalink
ChrLie's original runs a little faster than the revised version (for me the second method is about 30% slower). jjweimer's version, after adjusting to work on all the values in the 2D input wave, runs at about the same speed as ChrLie's revised version.
multithreading (by simply adding the threadsafe keyword before the function definition and multithread before the wave assignment) will make a difference, even for as few as 100 values in the input wave. About a factor of three speed difference for me.
implicit loops are appealingly succinct, but not necessarily more efficient than multiple assignments. I have caught myself putting conditionals in an implicit loop that don't depend on p [q, r,..], which is just bad.
October 8, 2024 at 02:26 am - Permalink