Scilab Bag Of Tricks: The Scilab-2.5.x IAQ (Infrequently Asked Questions) | ||
---|---|---|
Prev | Chapter 5. User Functions | Next |
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.
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 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.
![]() |
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. |
Function save copies an arbitrary Scilab variable or user-defined function to a file while compiling it to binary format. Given the function foo, we can generate its bin-file interactively with
-->foo foo = [y]=foo(x) -->save("foo.bin", foo) -->unix_w("ls -l foo.bin") -rw-rw-r-- 1 lvandijk users 204 Nov 14 09:30 foo.bin
foo must be accessible when save is executed, and the function's name in the call to save is not quoted.
Scilab supports the (undocumented) -comp command-line parameter to compile a sci-file into a bin-file.
lvandijk@hydra:~/hsc/scilab/src/minilib $ cat foo.sci function y = foo(x) y = 1 + x lvandijk@hydra:~/hsc/scilab/src/minilib $ scilab -comp foo.sci generating foo.bin lvandijk@hydra:~/hsc/scilab/src/minilib $ ls -l foo.* -rw-rw-r-- 1 lvandijk users 204 Nov 14 09:37 foo.bin -rw-r--r-- 1 lvandijk users 30 Nov 8 11:45 foo.sci
This way goes very well with Makefiles, as it implies the simple rule
# -*- makefile -*- %.bin: %.sci scilab -comp $<
The genlib function, which is described further down in Section 5.2.2.
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
![]() |
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:
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)
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.
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.
![]() |
Scilab's library mechanism only works well if
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
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.
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
![]() |
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.
[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. |