5.2. Libraries of sci-functions

Most users think there is no difference between loading a function immediately via getf, or loading it on-demand via lib. However, there are cases when getf and lib produce different results[1]. To stay clear of trouble it is useful to know what exactly getf and lib do.

5.2.1. getf vs. lib

getf. getf("filename") immediately, i.e. when getf is executed, loads all functions in filename. It is like saying: "Your functions – give them to me!" After a successful getf all functions from filename show up in the workspace. (Try who before and afterwards.)

getf is most useful during the development process, when functions are changed often. It also works well during production runs, if the number of functions loaded from the file is not too high. To suppress repeated loading of the same function-file, the following construct can be used:


if ~exists("myfunction") then
    getf("myfile.sci")
end

where myfunction is one of the functions in myfile.sci. Do not forget the quotes around the function's name in the call to exists!

lib. libvar = lib("lib-directory")[2] on the other hand does not load any function when the lib is executed. Instead, it marks all function-names listed in the file lib-directory/names as available for later loading.

Note

Note that lib-directory must end with a directory separator, i.e. a "/" in UNI*.

A function from libvar will be loaded when its – at that time undefined – name is first encountered during execution. It will never be re-loaded afterwards, even if the defining bin-file or the library change. The crucial word in the next-to-last sentence is "undefined": If the library function's name coincides with the name of a built-in function or an already defined user-function, the function definition from the library will not get loaded!

The function-names in lib-directory/names must refer to compiled functions, i.e. bin-files, in lib-directory. How to generate bin-files? Scilab offers three ways to convert a human readable sci-file into compiled bin-format.

Caution

The bin-format might change from one version to the next. When switching Scilab versions, it is advisable to delete all bin-files and regenerate them with the newer Scilab.

The names of the files which are part of the library are collected in names in a very simple format: one function-name per line, e.g.:


lvandijk@hydra:~/hsc/scilab/src/minilib $ cat names
bar
baz
foo
multi
myfun
Tip One function per file workaround
 

If a sci-file is intended to hold more than one function which all should be equally accessible from within Scilab, the following workaround can be used, given the operating system supports (symbolic) links.

Let us assume the multi-function sci-file is manyfun.sci. For every function fun1, fun2, ..., create a (symbolic) link to the "main" bin-file, manyfun.bin, like


    ln -s manyfun.bin fun1.bin
    ln -s manyfun.bin fun2.bin
    ...
   

and generate names afterwards.

The advantage of this hack is that it makes all functions from manyfun.bin available. Its disadvantage is that it is hard to maintain, e.g. if functions are added to or deleted from manyfun.sci and manyfun.bin has to be re-compiled, it is possible that some new links must be set up, and old links must be deleted.

There are plenty of possibilities to generate names outside Scilab, like e.g.


# -*- makefile -*-

sci_src:=$(wildcard *.sci)
sci_bin:=$(subst .sci,.bin,$(sci_src))

names: $(sci_bin)
        rm -f names
        for n in $(sci_bin); do echo $$(basename $$n .bin) >> names; done

or alternatively, if the shell has process substitution,


# -*- makefile -*-

names: $(sci_bin)
        sed -e 's/\.bin$//' <(ls -1 *.bin) > names

However, these solutions are unsatisfactory for large numbers of filenames. The reason for this shortcoming is the limited command-line length in most shells. In the first example make(1) expands $(sci_bin) to the names of all bin-files, in the second the shell does. Both might overrun the shell's command-line length limit. Therefore, a reliable solution does avoid expanding the filenames at the command-line. The following Perl-script, Example 5-5, demonstrates a more robust solution.

Example 5-5. Generate names for lib: gen-names


#!/usr/bin/perl -w
# name:      gen-names  --  generate "names" file for the use with
#                           Scilab's built-in lib function
# author:    L. van Dijk
# last rev.: Tue Nov 14 09:10:31 UTC 2000
# Perl ver.: 5.005_03

use strict;
use IO::File;

unless (@ARGV) { $ARGV[0] = '.' }
foreach my $dir_name (@ARGV) { process_directory($dir_name) }

sub process_directory {
    my $dir_name = shift;

    my $names = IO::File->new("> $dir_name/names")
      or die "Cannot open \'$dir_name/names\': $!\n";
    opendir DIR, $dir_name or die "Cannot open \'$dir_name\': $!\n";
    while (defined($_ = readdir DIR)) {
        next unless s/\.bin$//;
        print $names "$_\n";
    }
    closedir DIR;
}
   

gen-names is either called without an argument, then it creates names from the bin-filenames in the current working directory. If the arguments to gen-names are directories, they are processed in turn, each directory getting their respective names file.

After all desired sci-files have been converted to bin-files and the matching names-file has been written, the library is activated from the Scilab prompt:


-->minilib = lib("/home/lvandijk/hsc/scilab/src/minilib/")
 minilib  =

 Functions files location :/home/lvandijk/hsc/scilab/src/minilib/

 baz       bar       foo       myfun     multi

libvar, in our example minilib has got a special type, library. As you can see, minilib holds the information about the library, namely its defining directory and all functions it exports. To lookup the functions in a library, simply type the library variable's name. For a reverse lookup, i.e. searching to which library a function belongs, Scilab has the whereis function.


-->whereis myfun
 ans  =
 minilib   

The non-mandatory naming convention for library variables suggests to append lib to a library variable name, e.g. percentlib, fraclablib, soundlib, xdesslib.

libvar contains all necessary information about the library, and it is just an ordinary variable. Thus, it is lost when the Scilab session is closed. To make a library definition persistent, we have to perform two further steps:

  1. Translate libvar into a re-loadable format. Our old friend, the save function does that job for us.

    
-->save("/home/lvandijk/hsc/scilab/src/minilib/lib", minilib)
           
    
  2. Reload the library definition on every start of Scilab by placing the line

    
load("/home/lvandijk/hsc/scilab/src/minilib/lib");
           
    

    in the run-code file ~/.scilab. See also the primary Scilab run-code file, SCI/scilab.star.

The – again non-mandatory – file naming convention for saved library variables is to call them lib.

5.2.2. genlib

genlib reduces the process of compiling all necessary sci-files, generating names, and finally saving the library variable to one step:


    genlib("library-variable", lib-directory)

where the library variable names must be passed as a string. genlib always saves library-variable in lib-directory/lib.

5.2.3. Library Caveats

5.2.3.1. Library Files and Library Functions

Important

Scilab's library mechanism only works well if

  • every sci-file in the library contains only one function, and

  • the sci-file name without extension is identical to the function name in the file.

In other words: function foo must live in file foo.sci as a hermit.

Multiple functions per file are allowed; Scilab will not even generate a warning if a file with more than one function is used in a library. But the user should restrict the use of this feature to helper functions. A helper function is a function that only assists the main (not in the C-meaning) function, the one which gives the function-file the name. Special attention should be payed to the names of these "hobo" functions, which ride in the name of a real library function. They can cause name clashes with other functions. To avoid these underscores, dollar signs, or sharp symbols should be prepended to the function name, thereby faking a separate name space. This is demonstrated in the function file my_gamma.sci:


// file: my_gamma.sci

function a = my_gamma(z)
    if abs(z - int(z)) <= %eps and z > 0.0 then
        a = $_faculty(int(z) - 1)
    else
        a = gamma(z)
    end
endfunction

function k = $_faculty(n)               -- prepend "$_"
    k = prod(1 : n)
endfunction

// end file my_gamma.sci

5.2.3.2. On-Demand Loading

The on-demand loading of symbols from libraries can cause confusion (on the user's side) when a library symbol name clashes with the name of a "normal" variable.

Assume the library in the current directory holds the single function foo, which is defined as follows.


function y = foo(x)
    y = x + 0.5
endfunction

Consider a session that activates the library and defines a variable with the same name as the function from the library.


-->foolib = lib("./")
 foolib  =
 Functions files location :./
 foo       
 
-->foo
 foo  =
[y]=foo(x)
 
-->foo(2)
 ans  =
    2.5  

-->foo = 100
Warning :redefining function: foo
 foo  =
    100.  
 
-->foo
 foo  =
    100.  
 
-->foo(2)
 ans  =
    2.5  

Function foo in library foolib and variable foo peacefully coexist. If variable foo is defined before library foolib gets activated the same behavior results, only the warning message does not appear as library functions load silently.

Clearing foo removes the variable. If variable foo does not exist, clearing foo removes function foo, but the next time symbol foo is referred to again, function foo in library foolib will be loaded. To permanently clear function foo from the workspace, the association with library foolib must be removed first: clear foolib; clear foo. Now, function foo is undefined and its definition will not be reloaded from library foolib.

5.2.4. Loading Non-Functions With lib

The lib function is not picky in what it loads into the workspace. In the previous sections lib has been applied to directories that contain only files of compiled functions, this is ".bin" files. However, there is no restriction at all to save other entities than functions, for example variables, to ".bin" files. In case lib finds a saved variable in a directory, it will load it into the workspace as a local variable.


// my_sinc(x) uses the external parameter my_sinc_n
function y = my_sinc(x), ..
    if x == 0 do ..
        y = my_sinc_n, ..
    else ..
        y = sin(my_sinc_n * x) / (my_sinc_n * x), ..
    end, ..
endfunction  

save("my_sinc.bin", my_sinc)
clear my_sinc

my_sinc_n = 100;
save("my_sinc_n.bin", my_sinc_n)
clear my_sinc_n

Now assume that names in directory /tmp contains


my_sinc
my_sinc_n

Note

genlib only considers ".sci" files. To get a variable into a library, the variable has either to be defined in a separate ".sci" file, or it manually must be saveed and its name added to names.

Then both, my_sinc and my_sinc_n are loaded with the following lib call and can be used in the usual way:


-->sinc_lib = lib("/tmp/")
 sinc_lib  =
 Functions files location :/tmp/
 my_sinc_n           my_sinc

-->my_sinc_n
 my_sinc_n  =
    100.

-->my_sinc(1.0)
 ans  =
  - 0.0050637

-->my_sinc_n = 1
Warning :redefining function: my_sinc_n
 my_sinc_n  =
    1.

-->my_sinc(1.0)
 ans  =
    0.8414710

The above excerpt of a session transcript shows that the value of the variable my_sinc_n, which has been loaded with lib is used in the call of function my_sinc.

Notes

[1]

Thanks for pointing out the problems of Scilab's library handling in general and lib/genlib in particular go to Alexander Vigoder.

[2]

The online documentation, help lib, somewhat misleadingly calls lib-directory a lib_path, though it is only a single directory, not a path.