Functions
Table of contents
機能
目次
概要関数の定義出力引数の管理コールスタックのレベルreturn文pauseを使用した関数のデバッグ
このセクションでは、Scilab関数を紹介します。新しい関数を定義する方法と、
Scilabにロードする方法を説明します。関数の集合であるライブラリの作成とロード方法を説明します。また、入力引数と出力引数の管理方法も説明します。最後に、pause文を使用して関数をデバッグする方法を説明します。概要
さまざまなステップを再利用可能な関数にまとめることは、Scilab 開発者の最も一般的なタスクの 1 つです。
関数の最も単純な呼び出しシーケンスは次のとおりです: outvar = myfunction ( invar )
ここで、次のリストは構文で使用されるさまざまな変数を示しています:
myfunction は関数の名前、invar は入力引数の名前、outvar は出力引数の名前です。
入力引数の値は関数によって変更されませんが、
出力引数の値は関数によって実際に変更されます。実際、このドキュメントではすでにいくつかの関数を取り上げています。 y=sin(x) ステートメントの sin 関数は、
入力引数 x を受け取り、結果を出力引数 y に返します。Scilab
用語では、入力引数は右側、出力引数は左側と呼ばれます。関数は任意の数の入力引数と出力引数を持つことができるため、引数の数が固定の関数の完全な構文は次のとおりです: [o1, ..., on] = myfunction ( i1, ..., in )
入力引数と出力引数はコンマで区切られます。
入力引数は開き括弧と閉じ括弧で囲まれ、
出力引数は開き角括弧と閉じ角括弧で囲まれていることに注意してください。以下のScilabセッションでは、ヒルベルト行列のLU分解を計算する方法を示します。以下のセッションでは、2つの入力引数を取り、1つの行列を返すtestmatrix関数を使用して行列を作成する方法を示します。次に、1つの入力引数を取り、指定された出力変数に応じて2つまたは3つの引数を返すlu関数を使用します。3番目の引数Pが指定された場合、順列行列が返されます。In [1]: A = testmatrix("hilb",2) In [1]: A = testmatrix("hilb",2)
[L, U] = lu(A)
[L, U, P] = lu(A) Out [1]: A =
4. -6. -6. 12. L = -0.6666667 1. 1. 0. U = -6. 12. 0. 2. L = 1. 0. -0.6666667 1. U = -6. 12. 0. 2. P = 0. 1. 1. 0.
lu関数の挙動は、出力引数が3つ与えられた場合、実際には変化します。行列Lの2つの行が入れ替わっているのです。より具体的には、出力引数が2つ与えられた場合、分解はあ=LUが指定されている(文AL*Uはこれをチェックする)。出力引数が3つ与えられた場合、分解がP A=LUが指定されているかどうかを確認します( P A-L Uという文で確認できます)。実際には、出力引数が2つ指定されている場合、L行列に順列が適用されます。つまり、lu関数は入力引数と出力引数の数を認識し、それに応じてアルゴリズムを変更します。このドキュメントでは、この機能、つまり可変数の入力引数または出力引数を提供する方法については説明しません。ただし、Scilab言語ではこれが可能であることは覚えておいてください。
Scilab が関数を管理するために提供するコマンドは次のとおりです。
function : 関数定義を開くendfunction : 関数定義を閉じるargn : 関数呼び出しにおける入出力引数の数varargin : 入力引数リスト内の可変数の引数varargout : 出力引数リスト内の可変数の引数get_function_path : ライブラリ関数のソースファイルパスを取得するgetd : ディレクトリ内で定義されているすべての関数を取得するhead_comments : Scilab関数のヘッダーコメントを表示するマクロ変数: 関数の関数の変数
次のセクションでは、最もよく使用されるコマンドをいくつか紹介します。関数の定義
To define a new function, we use the function and endfunction Scilab keywords. In the following example, we define the function myfunction, which takes the input argument x, multiplies it by 2, and returns the value in the output argument y.function y = myfunction ( x ) y = 2 * x
endfunction
終了関数
function y = myfunction ( x ) という文は関数のヘッダーであり、関数本体は y = 2 * x という文で構成されます。関数本体には、1つ、2つ、または複数の文を含めることができます。Scilabでは、この関数を定義する方法が少なくとも3つあります。1
つ目の方法は、対話型モードでコンソールに直接スクリプトを入力することです。「function y = myfunction ( x )」という文を記述し、Enterキーを入力すると、Scilabはコンソールに新しい行を作成し、関数本体の入力を待ちます。
コンソールに「endfunction」という文を入力すると、Scilabは通常の編集モードに戻ります。もう1つの方法は、関数のソースコードがファイルで提供されている場合です。関数は一般的に非常に長く複雑なため、これが最も一般的なケースです。
関数定義をコンソールにコピー&ペーストするだけで済みます。関数定義が短い場合(通常、ソースコードが12行程度)は、この方法が非常に便利です。エディタを使えば、「Scilabにロード」機能のおかげで、これは非常に簡単です。exec関数も使用できます。exec関数は、コンソールで対話的に記述したかのようにファイルの内容を実行し、様々なScilabステートメントを1行ずつ表示します。ファイルには大量のソースコードが含まれている場合があり、出力が非常に長くなり、役に立たなくなることがあります。このような場合は、行末にセミコロン(;)を追加します。
これは、エディタの「エコーなしでファイルを実行」機能によって実行されます。
>exec(パス);
前の関数は、y=2*xという文で出力引数yの値を設定していることに注意してください。これは必須です。これを確認するために、次のスクリプトでは、変数zを設定し、出力引数yは設定しない関数を定義します。
function y = myfunction ( x )
z = 2 * x
endfunction
In the following session, we try to use our function with the input argument x=1.-->myfunction ( 1 )
!--error 4
Undefined variable: y
at line 4 of function myfunction called by :
myfunction ( 1 )
Indeed, the interpreter tells us that the output variable y has not been defined. Managing output arguments
In this section, we present the various ways to manage output arguments. A function may have zero or more input and/or output arguments. In the most simple case, the number of input and output arguments is pre-defined and using such a function is easy. But, as we are going to see, even such a simple function can be called in various ways.Assume that the function simplef is defined with 2 input arguments and 2 output arguments, as follows: In [2]: function [y1 , y2] = simplef ( x1, x2 ) y1 = 2 * x1 y2 = 3 * x2
endfunction In fact, the number of output arguments of such a function can be 0, 1 or 2. When there is no output argument, the value of the first output argument in stored in the ans variable. We may also set the variable y1 only. Finally, we may use all the output arguments, as expected. The following session presents all these calling sequences. In [3]: simplef ( 1 , 2 )
y1 = simplef ( 1 , 2 )
[y1,y2] = simplef ( 1 , 2 ) Out [3]: ans =
2. y1 = 2. y1 = 2. y2 = 6. We have seen that the most basic way of defining functions already allows to manage a variable number of output arguments. There is an even more flexible way of managing a variable number of input and output arguments, based on the argn, varargin and varargout variables. This more advanced topic will not be detailed in this document. Levels in the call stack Obviously, function calls can be nested, i.e. a function f can call a function g, which in turn calls a function h and so forth. When Scilab starts, the variables which are defined are at the global scope. When we are in a function which is called from the global scope, we are one level down
in the call stack. When nested function calls occur, the current level in the call stack is equal to the number of previously nested calls. The functions inquire about the state of the call stack:
whereami displays current instruction calling treewhere gets current instruction calling tree
In the following session, we define 3 functions which are calling one another and we use the function whereami to display the current instruction calling tree. In [4]: function y = fmain ( x )
y = 2 * flevel1 ( x )
endfunction
function y = flevel1 ( x )
y = 2 * flevel2 ( x )
endfunction
function y = flevel2 ( x )
y = 2 * x whereami()
endfunction When we call the function fmain, the following output is produced. As we can see, the 3 levels in the call stack are displayed, associated with the corresponding function. In [5]: fmain(1) Out [5]: whereami called at line 3 of macro flevel2
flevel2 called at line 2 of macro flevel1
flevel1 called at line 2 of macro fmain
ans = 8. In the previous example, the various calling levels are the following:
level 0 : the global level,level -1 : the body of the fmain function,level -2 : the body of the flevel1 function,level -3 : the body of the flevel2 function.
These calling levels are displayed in the prompt of the console when we interactively debug a function with the pause statement or with breakpoints. The return statement
Inside the body of a function, the return statement immediately stops the function, i.e. it immediately quits the current function. This statement can be used in cases where the remaining of the algorithm is not necessary.The following function computes the sum of integers from istart to iend. In regular situations,
it uses the sum function to perform its job. But if the istart variable is negative or if the
istart<=iend condition is not satisfied, the output variable y is set to 0 and the function immediately returns. In [6]: function y = mysum ( istart , iend )
if ( istart < 0 ) then y = 0 return end if ( iend < istart ) then y = 0 return end y = sum ( istart : iend )
endfunction The following session checks that the return statement is correctly used by the mysum function. In [7]: mysum ( 1 , 5 )
mysum(-1, 5)
mysum(2, 1) Out [7]: ans =
15. ans = 0. ans = 0. Some developers state that using several return statements in a function is generally a bad practice.
Indeed, we must take into account the increased difficulty of debugging such a function, because the algorithm may suddenly quit the body of the function. The user may get confused about what exactly caused the function to return.This is why, in practice, the return statement should be used with care, and certainly not in every function. The rule to follow is that the function should return only at its very last line.
Still, in particular situations, using return can actually greatly simplify the algorithm, while avoiding return would require writing a lot of unnecessary source code. Debugging functions with pause
In this section, we present simple debugging methods that fix most simple bugs in a convenient and efficient way which are:
pause waits for interactive user input resume resumes execution and copy some local variables abort interrupts evaluation
A Scilab session usually consists in the definition of new algorithms by the creation of new functions.
It often happens that a syntax error or an error in the algorithm produces a wrong result.Consider the problem, the sum of integers from istart to iend. Again, this simple example is chosen for demonstration purposes, since the sum function performs it directly.The following function mysum contains a bug: the second argument "foo" passed to the sum function has no meaning in this context. In [8]: function y = mysum ( istart , iend )
y = sum ( iend : istart , "foo" )
endfunction Out [8]: Warning : redefining function: mysum . Use funcprot(0) to avoid this message
The following session shows what happens when we use the mysum function. In [9]: mysum ( 1 , 10 ) Out [9]: at line 2 of function mysum
sum: Wrong value for input argument #2: Must be in the set {"*","r","c","m","native","double"}.
In order to interactively find the problem, we place a pause statement inside the body of the function.function y = mysum ( istart , iend ) pause y = sum ( iend : istart , "foo" )
endfunction
We now call the function mysum again with the same input arguments.-->mysum ( 1 , 10 )
Type 'resume' or 'abort' to return to standard level prompt.
- 1->
We are now interactively located in the body of the mysum function.
The prompt "-1->" indicates that the current call stack is at level -1.
We can check the value of the variables istart and iend by simply typing their names in the console. -1->istart
istart = 1.
- 1->iend
iend = 10.
In order to progress in our function, we can copy and paste the statements and see what happens interactively, as in the following session.-1->y = sum ( iend : istart , "foo" )
y = sum ( iend : istart , "foo" )
!--error 44
Wrong argument 2.
We can see that the call to the sum function does not behave how we might expect. The "foo" input argument is definitely a bug: we remove it. -1->y = sum ( iend : istart )
y = 0.
After the first revision, the call to the sum function is now syntactically correct. But the result is still wrong, since the expected result in this case is 55. We see that the istart and iend variables have been swapped. We correct the function call and check that the fixed version behaves as expected-1->y = sum ( istart : iend )
y = 55.
The result is now correct. In order to get back to the zero level, we now use the abort statement, which interrupts the sequence and immediately returns to the global level.-1->abort
- >
The --> prompt confirms that we are now back at the zero level in the call stack.We fix the function definition, which becomes: function y = mysum ( istart , iend )
pause y = sum ( istart : iend )
endfunction
In order to check our bugfix, we call the function again.-->mysum ( 1 , 10 )
Type 'resume' or 'abort' to return to standard level prompt.
- 1->
We are now confident about our code, so that we use the resume statement, which lets Scilab execute the code as usual.-->mysum ( 1 , 10 )
- 1->resume
ans = 55.
The result is correct. All we have to do is to remove the pause statement from the function definition.function y = mysum ( istart , iend )
y = sum ( istart : iend )
endfunction
In this section, we have seen that, used in combination, the pause, resume and abort statements are a very effective way to interactively debug a function. In fact, our example is very simple and the method we presented may appear to be too simple to be convenient. This is not the case.
In practice, the pause statement has proved to be a very fast way to find and fix bugs, even in very complex situations.