.. highlightlang:: us .. _us-functions: Functions ========= A function is a name for a sequence of statements. You can use functions as a black box. You do not need to know how the function works. For example, to use the :ref:`sin` function you only need to know the name and the interface of the function. .. index:: dB, log10, symbols, SYM_BUILDIN .. index:: def .. _def: .. _function-syntax: Function Syntax --------------- In UniScript, a function is easily defined. Example:: def dB(rvSignal) { return 20.0 .* log10(rvSignal); } The keyword :keyword:`def` is followed by the function name. The function name is followed by a list of comma-separated arguments enclosed in parentheses. In our example, the function name is ``dB`` and the argument has the name ``rvSignal``. The function has one statement which returns the result to the caller of the ``dB`` function. The function call can look like this:: rvdB = dB(1:100); The values 1 to 100 will be converted into dB (Decibel) values. A function in UniScript has the following syntax:: def name (parameter-list) { statements } The rules for function names are the same as for variable names. The name must be a sequence of letters, digits and underscores, however, it may not begin with a digit. The length of names is not limited in UniScript, but only the first 80 characters are significant. Case is also significant in names. The name must be distinct from all variables or built in function names. A built in function is a function written in in C, C++ or FORTRAN. They are located in Dynamic Link Libraries (DLLs) and are loaded when UniScript starts. A list of all built in functions can be received with the function call ``symbols(SYM_BUILTIN)``. The function name is followed by a comma separated parameter list enclosed in parentheses. The parameter list can have between 0 and 32 elements. The shortest form of a function in UniScript is:: def f() { } The parentheses and the braces are necessary for syntax reasons. When this function is called, it returns the value 0.0. .. _function-parameters-and-arguments: Function Parameters and Arguments --------------------------------- Values which are passed to a function are called arguments. Parameters are the names of the arguments. In UniScript, the number of function parameters is limited to 32. Example:: def dB(rvSignal) { return 20.0 .* log10(rvSignal); } db(1:100) The value ``1:100`` is an argument, ``rvSignal`` is the function parameter. A function can be called with less arguments than the function has parameters. Example:: def Test(a, b, c) { return 1; } Test(); Test(1); Test(1,2); Test(1,2,3); Test(1,2,3,4); Except for the last function call, all of the function calls above are valid. The number of arguments can be checked inside a function with the function :ref:`nargsin`:: def Test(a, b, c, d, e, f, g) { nArgs = nargsin(); printf("The function was called with %d out of 7" + .. " possible arguments\n", nArgs); } Function parameters which have not been passed to the function cannot be accessed. To do so will produce an error message. If the function call is ``Test(1, 2, 3)``, the parameters ``d`` - ``g`` are not available and should not be used. .. index:: global .. index:: Local Variables .. _global-and-local-variables: Global and Local Variables -------------------------- Normally, all variables used inside a function are local variables. These variables will be created when the function is called and will be destroyed when the function is finished. Local variables are not known outside the function. A variable can be declared as global to avoid this behavior. Global variables can be accessed from any UniScript function. The global variable must be declared at the beginning of the function in order to access it. Example:: def Test() { global Glob1, Glob2; Glob1 = 1; Glob2 = 2; Local1 = 3; Local2 = 4; print Glob1, Glob2, Local1, Local2; } Glob1 = 5; Glob2 = 6; Local1 = 7; Local2 = 8; print Global1, Global2, Local1, Local2; Test(); print Global1, Global2, Local1, Local2; If you execute this program, it will print the numbers 5, 6, 7, 8. In the function ``Test()``, it will print the numbers 1, 2, 3, 4 and in the last :keyword:`print` statement, the numbers 1, 2, 7, 8 will be printed. The function ``Test()`` has changed the variables ``Glob1`` and ``Glob2``. The variables ``Local1`` and ``Local2`` were not changed by the program ``Test()``. .. index:: return .. _return: .. _the-return-statement: The return Statement -------------------- The :keyword:`return` statement returns values to the calling function and terminates the execution of the called function. The :keyword:`return` statement has the following syntax:: return expression; If the expression is empty, the function returns the value 0.0. A function without a :keyword:`return` statement returns the default return value 0.0. Example:: def Test(x) { if ((x % 2) == 0) { return "x is divisible by 2 without a remainder"; } return "x is NOT divisible by 2 without a remainder"; } Test(4) Test(5) If the argument of the function ``Test()`` is divisible by 2, the first return statement will be executed and the function terminated. Otherwise, the second return statement will be executed. .. index:: multiple return values, eig .. _multiple-return-values: Multiple Return Values ---------------------- All of the functions we have used so far, such as :ref:`sin`, have only one return value. However, functions in UniScript can return multiple values. Example: .. index:: eig If you call the :ref:`eig` function in the form :: * m = [4,7;2,5] * eig(m) 8.2749 + 0.0000i 0.7251 + 0.0000i it returns the eigenvalues of the matrix ``m``. If you call the function in the following form :: * = eig(m) it will return 2 variables (the eigenvalue ``e`` and the eigenvector ``v``). The syntax for defining functions that return multiple values is :: def = name(argument list) { statements } The return list is a list of variable names separated by commas. The function :ref:`nargsout` returns the number of values that are expected to return. Return variables which have not been passed to the function cannot be accessed. To do so will produce an error message. :: def = Test(e,f,g,h,i) { nArgsIn = nargsin(); nArgsOut = nargsout(); printf("The function was called with %d out of 5 " + .. "possible arguments\n", nArgsIn); printf("The function was called with %d out of 4 " + .. "possible return variables\n", .. nArgsOut); } Instead of using multiple return values an object can be returned as shown in the following example (see also :ref:`uniscript-objects`). Example:: def Test(p1, p2) { o = [.]; // Create objekt o.a = 123; o.b = "Hello"; return o; } **Note**: The return variables enclosed in angle brackets ``< >`` cannot be used in the argument list of the function definition. Two or more return values should not be used. Instead an object can be returned (see :ref:`uniscript-objects`). Example:: def Test(p1, p2) { o = [.]; // Objekt erzeugen o.a = 123; o.b = "Hello"; return o; } .. index:: DLL's, FindWindow, C, FORTRAN, startup.ic .. _calling-functions-from-dll`s: Calling Functions from DLL`s ---------------------------- DLL is an abbreviation for Dynamic Link Library. A DLL is a collection of functions created with a compiler. With UniScript, functions located in DLLs can be called directly except when the function's parameter list contains structures or pointers to functions. In this case, you still have to use a C or FORTRAN compiler to create a DLL to enable UniScript to call this function. We would like to show you the use of the interface with some examples. FindWindow ^^^^^^^^^^ The first example is the function ``FindWindow()``. This function is located in a DLL which belongs to the Windows Operating System. The function can be used to check if a certain program has already been started. Before the function can be used, it must be registered. This means, we have to tell UniScript in which DLL the function is located, what the internal name of the function is and what function parameter and return values the function has. The data types of the parameters are needed so that UniScript can convert the values passed to the function to the correct type. To register a new function, use :ref:`RegisterFunction`. This function must be called only once for each new function. A good place to invoke the function is the startup file :file:`startup.ic`. :: RegisterFunction("USER32", "FindWindowA", .. "uint", "FindWindow", char* , char*); The first parameter is the name of the DLL. The function ``FindWindow()`` is located in the DLL :file:`USER32.DLL` in one of the Windows directories. To find the DLL in which a function is located, refer to the Windows Software Development Kits (SDK) documentation. SDK documentation is part of a Windows compiler documentation. The second parameter is the internal name of the function. The ``FindWindow()`` function has the internal name ``FindWindowA``. This information is also found in the SDK documentation. The third parameter is a string which specifies the data type of the return value. The SDK documentation specifies the data type of the return value as a ``HWND`` (handle to a Window). Because the :ref:`RegisterFunction` does not know this data type, we use the data type ``"uint"``. An ``"uint"`` is an unsigned 32 Bits integer. The data types that can be used for the :ref:`RegisterFunction` are specified in the UniScript Reference. The fourth parameter is the name of the function you want to call from UniScript. You can specify any name that meets the rules of UniScript function names. The fifth parameter is a string vector which specifies the data types of the function parameters. The function ``FindWindow`` has two parameters. The first one is a pointer to a string which specifies the internal name of the window, and the second parameter is a pointer to a string which specifies the window title. The internal window name for Excel is, for example, ``"XLMAIN"``. For MS-WORD it is ``"OpusApp"`` and for UniPlot it is ``":UniPlot:"``. After we have registered the new function, we are able to call it. When the function returns a value not equal to 0, it means that Excel has already been started. If the return value is 0, Excel has not been started. :: // Find the Excel Window. The second parameter // can be set to 0 (see SDK documentation) h = FindWindow("XLMAIN", 0); if (h == 0) { // Window not found, i.e. Excel not started // Start Excel: system("excel", SW_SHOW, TRUE); } // at this point Excel is running .. index:: DGEMM, FORTRAN, RegisterFunction DGEMM ^^^^^ The next example is a more complicated call of a mathematical function which is located in the UniScript DLL :file:`rs_math.dll`. The function evaluates the following term: :: C = alpha * A * B + beta * C ``alpha`` and ``beta`` are scalar values, ``A``, ``B`` and ``C`` are vectors or matrices. The function is only an example to show how FORTRAN functions can be called from UniScript because the term :: C = alpha * A * B + beat * C can also be evaluated directly by UniScript without calling a function. However, it is a good example to show the calling of functions of this kind. The FORTRAN function has the following form: SUBROUTINE DGEMM (TRANSA, TRANSB, M, N, K, ALPHA, A, LDA, B, LDB, BETA, C, LDC) The function belongs to the BLAS Library (Basic Linear Algebra System). Functions in DLLs must have a so called C interface which looks like the following:: int dgemm_(char *transa, char *transb, int *m, int *n, int *k, double *alpha, double *a, int *lda, double *b, int *ldb, double *beta, double *c, int *ldc); The corresponding UniScript registration call for this function has the following form:: RegisterFunction("rs_math.dll", "dgemm_", "int", .. "dgemm", .. "char *transa", "char *transb", "int *m", "int *n", .. "int *k", "double *alpha", "double *a", .. "int *lda", "double *b", "int *ldb", .. "double *beta", "double *c", "int *ldc"); We do not call the ``_dgemm()`` function directly because UniScript can only perform a very limited number of error checks when calling external DLL functions. A faulty function call can lead to a "crash" of UniPlot, a so called "Memory Access Violation". Under Windows 95/98 it can also lead to a crash of Windows itself so that you have to reboot your computer. Under Windows NT, the applications run in a protected mode so that such an error will only affect UniPlot. To avoid such a problem, we will write a wrapper function for ``_dgemm()``. To simplify the problem we set ``alpha`` to 1 and ``beta`` to 0 in our formula:: C = alpha * A * B + beta * C // C = A * B def Multiply(a, b) { if (nargsin() != 2) { error("number of args"); } if (type(a) != "real" || type(b) != "real") { error("type error"); } nra = nr(a); nrb = nr(b); nca = nc(a); ncb = nc(b); if (nra != ncb) { error("dimensions must match"); } c = zeros(nra, ncb); _dgemm("N", "N", nra, ncb, nca, 1.0, .. a, nra, b, nrb, 0.0, c, nra); return c; } The following lines are a test of the new interface and will compare the result with the built in multiplication operator. :: a = rand(2, 3); b = rand(3, 2); x1 = Multiply(a, b) x2 = a * b; print x1-x2; .. index:: GetCurrentDirectory, SetCurrentDirectory GetCurrentDirectory, SetCurrentDirectory ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ We will now write two functions to change directories or return the current working directory respectively. ``gwd()`` (get working directory) should return the current working directory. ``cd()`` (change directory) should change the directory. The corresponding Windows functions are ``GetCurrentDirectoryA()`` and ``SetCurrentDirectoryA()``. ``GetCurrentDirectoryA()`` has two parameters. The first parameter specifies the buffer size in which the function will write the directory name. The second parameter is a pointer to the buffer. The function will not write more characters into the buffer than the first parameter specifies. The function returns the number of actually written characters. The registration of this function looks like the following:: RegisterFunction("KERNEL32", "GetCurrentDirectoryA",.. "uint", "__GetCurDir", ["uint", "char*"]); The registration for ``SetCurrentDirectory()`` is:: RegisterFunction("KERNEL32", "SetCurrentDirectoryA",.. "uint", "__SetCurDir", "char*"); We will now write the wrappers for these functions:: def gwd() { ss = "012345678901234567890123456789"; ssBuffer = ""; for (i in 1:10) ssBuffer = ssBuffer + ss; GetCurDir(strlen(ssBuffer), ssBuffer) return ssBuffer; } The first three lines of the function ``gwd()`` will create a buffer for 300 characters which will then be filled by the ``GetCurrentDirectoryA()`` function. The function ``gwd()`` returns the buffer in the last line. The function ``SetCurrentDirectoryA()`` receives a similar wrapper. The two :keyword:`if` statements avoid the problem of the function being called with a wrong number of arguments. :: def cd(ssDir) { if (nargsin() != 1) { error("usage: cd (ssDir)") } if (type(ssDir) != "string") { error("usage: cd (ssDir)") } return SetCurDir(ssDir); } Finally, we can test the new functions in the command window:: * cd ("c:/") 1.0000 * cd () >>> cd : usage: cd (ssDir) * gwd() c:\ * cd("d:/uniplot/script") 0.0000 * gwd() c:\ * cd("d:/uniplot/script") 1.0000 * gwd() d:\uniplot\script .. index:: COM .. index:: COM-Interface .. index:: ActiveX .. index:: dispatch interface, CreateObject .. _com-interface: .. _activex-interface: COM-Interface ------------- As of Version 3.0 UniScript can access objects that have a so called dispatch interface. Below are some examples of how the new interface can be used. .. index:: Excel Access Excel ^^^^^^^^^^^^ In the first example we want data to be inserted into a Microsoft Excel spreadsheet. First Excel must be started by calling the :ref:`CreateObject` function. :ref:`CreateObject` returns a pointer (a reference) to the Excel instance. The function expects the server name of the program that should be started as the argument. For Excel the server name is ``"Excel.Application"``. :: xls = CreateObject("Excel.Application") xls.Visible = 1 The second line (``xls.Visible = 1``) will make the application visible. To access the methods and properties of an object use a period (.). In the following example UniScript calls a method (function) to add a new workbook to the existing document:: wb = xls.Workbooks wb.Add(); You can call both methods in one statement:: xls.Workbooks.Add() The following lines will fill the sheet:: xls.ActiveSheet.Range("a1").Value = "x" xls.ActiveSheet.Range("b1").Value = "sin(x)" x = linspace(0, 2*PI) y = sin(x) xls.ActiveSheet.Range("a2:a101").Value = x' xls.ActiveSheet.Range("b2:b101").Value = y' .. index:: DAO .. index:: ADO Data Base Access ^^^^^^^^^^^^^^^^ In the following example, we will use Microsoft ADO (ActiveX Data Objects) to access the data of a Microsoft Excel XLSX file like a database table with SQL. :: // create an Excel 2007 file with the following content and // name the sheet Sheet1: /* a b c 1 2 test1 4 5 test2 3 1 test3 */ def ado_Excel_Test() { // Must be an XLSX file, slash or double backslash ssExcelFile = "c:\\excelfile.xlsx"; ssSheetName = "Sheet1"; // HDR=Yes : First row are the column names of the table strCnn = "Provider=Microsoft.ACE.OLEDB.12.0;Data " + ... "Source=%s;" + .. "Extended Properties=\"Excel 12.0 Xml;HDR=Yes;IMEX=1\";" strCnn = sprintf(strCnn, ssExcelFile); db = db_create("ado"); if (db.connect(strCnn) == 0) { MessageBoxError("Cannot connect: %s", strCnn); return "" } // $-sign is neccessary for the table name // return all data ssSQL = sprintf("SELECT * FROM [%s$];", ssSheetName); smData = db.exec(ssSQL); // return all columns and all records where a > 1 ssSQL = sprintf("SELECT * FROM [%s$] Where a > 1;", ssSheetName); smData = db.exec(ssSQL); // return column b and c where column a > 1 ssSQL = sprintf("SELECT b,c FROM [%s$] Where a > 1;", ssSheetName); smData = db.exec(ssSQL); return smData; } In the following example, we will use Microsoft Data Access Objects (DAO) to read data from a Microsoft Excel table. DAO is not available in 64-bit Windows. :: dao = CreateObject("Dao.DBEngine.36"); if (dao == 0) { error("Cannot create Dao.DBEngine.3.6"); } ws = dao.CreateWorkspace("", "Admin", "", 2); ssFile = GetRootDirectory()+ "samples\ole\test.xls" dbs = ws.OpenDatabase(ssFile, 0, 0, "Excel 8.0;HDR=No;"); m = dbs.OpenRecordset("test$").GetRows(1000); print m .. index:: variant matrix Variant Matrix ^^^^^^^^^^^^^^ An Excel table can contain different data types. In the example above we wrote the string "x" into the cell A1. We wrote floating point numbers into the cells A2 to A101. In a data base each column can have a different data type, e.g. the first column is the date, the second column an integer and the third column is a string with 80 characters. To enable UniScript to deal with data matrices where elements can have different data types we added variant matrices to UniScript. A variant matrix can contain elements of the following 4 data types: .. list-table:: :header-rows: 1 * - Type - Meaning * - real - Double precision values with 8 bytes. * - complex - Complex values. Two double precision values. A complex value has 16 Bytes. * - string - A string with up to 2 times 31 characters. * - object - Reference to an ActiveX object. The following example creates an column vector with 3 real values, one complex value, one string and one object reference. :: * xls = CreateObject("Excel.Application"); * v = [ConvertToVariant([1,2,3]), 4+5i, "Hello", xls]' * v 1.0000 2.0000 3.0000 4.0000 + 5.0000i Hello (0x001B6FC4) The element access for variant matrices is identical to any other matrix element access in UniScript. Variant matrices cannot be used in calculations. Comparison operators for variant matrices are not supported. .. index:: VariantConvert, real The following statement will therefore create a runtime error:: a = [1, "Hallo"] b = a if (a == b) MessageBox("a not equal to b") To use data from variant matrices in computations, the matrices must be converted to real matrices. This can be performed with the :ref:`VariantConvert` function. :: a = [1, "Hallo"] b = a print VariantConvert(a[1]) + VariantConvert(b[1]) The inverse function to :ref:`VariantConvert` is :ref:`ConvertToVariant`. :ref:`VariantGetType` returns a matrix with the type of each element. The UniScript variant matrix is not identical to the data type Variant of Visual-Basic or the ActiveX interface. The ActiveX variant data type knows the following data types: .. list-table:: :header-rows: 1 * - Type No. - Name - Description * - 0 - VT_EMPTY - empty. * - 1 - VT_NULL - Null. * - 2 - VT_I2 - 2-byte signed int. * - 3 - VT_I4 - 4-byte signed int. * - 4 - VT_R4 - 4-byte real. * - 5 - VT_R8 - 8-byte real. * - 6 - VT_CY - Currency. * - 7 - VT_DATE - Date/Time. * - 8 - VT_BSTR - Binary string. * - 10 - VT_ERROR - SCODE. * - 11 - VT_BOOL - Boolean. * - 17 - VT_UI1 - Unsigned char. When UniScript uses a property or a method of the COM interface, UniScript must perform a data type conversion from the UniScript data type to the COM data type. Data types with the numbers 2, 3, 4, 5, 11, 17 will be converted to the UniScript-real data type (Double precision value). Type number 8 will be converted into a UniScript-String. The types 0, 1, 6, 7 will be converted to a complex number where the real part is the value and the imaginary part is the type number. Example: An Excel table contains the following data in the first column:: x 1,23 01.01.2000 #DIV/0! FALSCH UniScript will return the following vector:: * v = xls.ActiveSheet.Range("a1:a6").Value' * print v x 1.2300 0.0000 + 0.0000i 36526.0000 + 7.0000i -2.147e+009 + 10i 0.0000 Cell A3 is empty. For this element UniScript returns the complex number ``0+0i``. Cell A4 contains a date. For this element UniScript will return the complex number ``36526+7i``. To convert the date into an string use ``DT_Format(real(v[4]))``. The cell A5 (-2.147e+009 + 10i) contains an error code. This is the Excel error code for the Excel formula ``=1/0``. .. index:: C, C++, VBScript, JavaScript, Excel 2000 UniPlot as an COM Server ^^^^^^^^^^^^^^^^^^^^^^^^ UniPlot can be used as an COM server (OLE Server or Automation Server). This means that you can use UniPlot/UniScript functions in other applications. UniPlot uses a dual interface, i.e. UniPlot can be used by script languages like VBScript, JavaScript, Visual Basic for Applications as well as languages like C, C++ or Visual Basic. The following examples will show you how to use UniPlot with Excel. To try out the examples, you need Excel 97 or Excel 2000. Setup ^^^^^ To setup the examples, execute the following steps: * If UniPlot is running, quit UniPlot. * The examples are located in the directory :file:`UniPlot\\Samples\\ole`. Copy the UniScript files that begin with ``AX-`` into UniPlot Autoload directory. * Open the DOS command window. * Go to the :file:`\\UniPlot\\Program` directory and start UniPlot with the following command:: uniplot /regserver This will register UniPlot as an OLE-Server. .. index:: Autoload * After you are finished evaluating the examples it is recommended to remove the :file:`AX-*.ic` files from the :file:`UniPlot/AutoLoad` directory. Example 1 (AX-Data) ^^^^^^^^^^^^^^^^^^^ The following example shows how a UniScript Function can be called by Excel and how data can be passed to the function. To execute the function, do the following: * Start UniPlot. Open the UniScript Command window (:ref:`viewcommand-window`). * Start Excel. Open the file :file:`UniPlot\\Samples\\Ole\\AX-Data.xls`. Excel may display a dialogbox with a message saying that the file contains macros. Select the button **Activate Macros**. Arrange the two applications side by side on your monitor. * Select some cells in the Excel sheet. You'll see that the data will be displayed in the UniScript Command window when you change the selection. How does the data exchange between Excel and UniPlot/UniScript work? Two scripts have been written. The Visual-Basic for Applications script, which is located in the Excel file :file:`UniPlot\\Samples\\Ole\\AX-Data.xls` and a UniScript function that you can find in the file :file:`UniPlot\\AutoLoad\\AX-Data.ic`. Together these small script programs allow the data exchange between Excel and UniPlot Lets have a look at the Excel script. To view the program open the Excel file and select **Tools=>Macro=>Visual Basic-Editor** (Alt+F11). .. highlightlang:: vb.net :: Dim app As Object Private Sub Worksheet_SelectionChange(ByVal Target As Excel.Range) On Error GoTo Error Set app = CreateObject("UniPlot.Application") app.Visible = 1 n = app.Call("AX_DataTest", Target) Error: End Sub The code line ``Dim app As Object`` creates a global variable. It was not created inside the function to avoid it being destroyed when the function exits. The ``Worksheet_SelectionChange`` function will be called by Excel when the selection in the table changes. A range object is passed as the first parameter by the name ``Target``. ``Set app = CreateObject("UniPlot.Application")`` creates a reference to a running UniPlot instance. The function is equivalent to the :ref:`CreateObject` function in UniScript. The ``app.Visible = 1`` statement makes the UniPlot window visible if it was hidden. The code line ``n = app.Call("AX_DataTest", Target)`` calls a UniScript function. The first parameter is a UniScript function name, (in this example ``AX_DataTest``) followed by the function parameters. The number of parameters is limited to 16. Now lets have a look at the UniScript function ``AX_DataTest``. The first parameter is the Excel range object. Here we use only the Row and Count property. You will find a complete description of all properties and methods of the range object in the Excel Online help system. :: def AX_DataTest(t) { if (t.Rows.Count > 100) { print "Rows > 100" } else { print t.Value } } Example 2 (AX-Curv) ^^^^^^^^^^^^^^^^^^^ To run the second example, do the following: * If UniPlot is running, quit UniPlot. * Start Excel. Open the file :file:` UniPlot\\Samples\\Ole\\AX-Curv.xls`. Excel may display a dialog box with a message saying that the file contains macros. Select the button **Activate Macros**. * The second spreadsheet contains two UniPlot objects each showing one diagram. To send data to the left diagram select the values 1, 4, 2, 2, etc. Data will be sent to the right diagram when the first value is not equal to 1. .. image:: S:/uniplot-obj/images/OLE_Excel_Curve.* .. highlightlang:: vb.net :: Private Sub Worksheet_SelectionChange(ByVal Target As Excel.Range) Dim arr(100) As Double On Error GoTo Error Worksheets("Sheet 1").OLEObjects(1).Enabled = True Worksheets("Sheet 1").OLEObjects(1).AutoLoad = True Worksheets("Sheet 1").OLEObjects(2).Enabled = True Worksheets("Sheet 1").OLEObjects(2).AutoLoad = True hDocLeft = Worksheets(1).OLEObjects(1).Object.Handle hDocRight = Worksheets(1).OLEObjects(2).Object.Handle t1$ = Cells(22, 2).Value t2$ = Cells(23, 2).Value i = 0 For Each Cell In Target.Cells arr(i) = CDbl(Cell.Value) i = i + 1 Next Set app = Worksheets("Sheet 1").OLEObjects(1).Object.Application n = app.Call("AX_CurveTest", hDocLeft, hDocRight, arr, i, t1$, t2$) Error: End Sub .. highlightlang:: us :: def AX_CurveTest(hDoc1, hDoc2, y, n, t1, t2) { if (y[1] == 1.0) { hPage = DocGetActivePage(hDoc1); } else { hPage = DocGetActivePage(hDoc2); } if (hPage == 0) { return 0; } hLayer = PageGetAllDataLayers(hPage) hData = LayerGetAllDatasets(hLayer); if (hData == 0) { hData = XYCreate("test", 1, 1); LayerAddDataset(hLayer, hData); } if (n > 1) { x = 1:n; ret = XYSetData(hData, x, y[x]); } LayerAutoScale(hLayer) LayerSetAxisTitle(hLayer, "x", t1) LayerSetAxisTitle(hLayer, "y", t2) __FF_FieldUpdate(0, hPage, 0, 0, 0); PageReplot(hPage); return n; } .. index:: Debugger .. _the-debugger: The Debugger ------------ Debugging is the process of correcting or modifying the code in a UniScript function. .. image:: S:/uniplot-obj/images/Debugger.* * Open the file :file:`UniPlot\\Script\\do_new.ic`. * Set the cursor in line 62 (``def _DoFileNew()``). Press :kbd:`F9` (**Toggle-Breakpoint**) to set a breakpoint. At the left side a break point simble will be set (red dot). * Execute the command :ref:`filenew`. The execution will stop at the breakpoint and a yellow arrow will be displayed. * With :kbd:`F10` to single-step through instructions in the program. With :kbd:`F5` you can run to the next break point. The UniScript debugger supports the following functions: Example of debbuging a UniScript function: :kbd:`F5` Go: Executes code from the current statement until a breakpoint or the end of the program is reached, or until the application pauses for user input. :kbd:`F9` Set/remove a break point. :kbd:`Ctrl+F9` Remove all break points. :kbd:`F10` Step Over: Single-steps through instructions in the program. :kbd:`F11` Step Into: Single-steps through instructions in the program, and enters each function call that is encountered. .. index:: recursive function calls, call by reference, call by value .. _some-annotations-about-functions: Some Annotations about Functions -------------------------------- * UniScript allows recursive function calls. A recursive function is one which calls itself either directly or indirectly. * In contrast to C, UniScript passes function parameters by reference and not by value. This makes it possible for a function to change the values of a parameter. So be careful and create copies of the parameters:: // INCORRECT def asin(x) { // WRONG !!!!!! x = -1i .* log (1i .* x + sqrt(1 - x .* x)); if (all(all(real(x) == x))) { return real(x); } else { return x; } } // CORRECT def asin(xx) { x = xx; // Use a copy x = -1i .* log (1i .* x + sqrt(1 - x .* x)); if (all(all(real(x) == x))) { return real(x); } else { return x; } } .. index:: startup.ic, rs_sl.icl .. index:: what, load * The function :ref:`what` returns a list of all loaded functions. * The UniScript source code file can be loaded with the :ref:`load` function. The function call can be included into the file :file:`startup.ic`. * To reduce the loading time of the UniScript user function, a UniScript library is loaded during the execution of the startup file. The name of the function which loads the library is :ref:`loadiclib`. To create a UniScript library (default name: :file:`rs_sl.icl`), use the following command:: saveiclib("c:/uniplot/program/rs_sl.icl") :sub:`id-618984`