はじめての Octave プログラミング

この Notebook は,

のプログラミング部分に相当する内容を JavaScript のかわりに Octave を使って書いてみたものです。

章番号はオリジナルの従来テキストに対応するように8章からはじめていますので,従来テキストを使用して授業を展開してきた教員や受講学生にも対応がわかりやすいかと思います。

また,テキストや授業と関係なく,初めて Jupyter Notebook で Octave を利用する学生や教員にとっても参考になるかも知れません。

この Notebook では,プログラミング言語としての共通部分に焦点を絞って解説します。

では,はじめましょう。

はじめてのプログラミング

エンゲル係数とは,家計の消費支出に占める飲食費(食料費,食費)の割合(パーセント単位)です。

消費支出 100,000円,飲食費 40,000円の人のエンゲル係数を求める例。Octave では以下のような電卓的な使い方ですぐ答えが出ます。

本稿のように Jupyter Notebook 環境では,以下の1行を書いたあとに,Shift キーを押しながら Enter キー(または Return キー)を押して実行します。

In [1]:
40000/100000 * 100
ans = 40

ここからは,単に電卓的に使うだけではなく,応用・発展も見据えて以下のようなプログラム文を作成して実行することにします。

以下の例では,

  1. 1行目で変数 s1 に消費支出である 100000(円)という数値を代入し,
  2. 2行目で変数 s2 に飲食費である 40000(円)という数値を代入し,
  3. 3行目でエンゲル係数を計算して,変数 x1 に代入し,
  4. 4行目でエンゲル係数の値 x1 を表示させています。

Octave では,文末に ; をつけると結果が表示されません。

In [2]:
s1 = 100000;
s2 = 40000;
x1 = s2*100/s1;
disp(x1)
40

エラーとデバグ

意に反して,プログラムがうまく動作しない場合があります。たとえば,上記の 3行目を以下のように書いた場合。

掛け算を示す演算子は *,割り算を示す演算子は / であるところを誤って,×÷ と書くと,これらの記号は Python では演算子として定義されていないのでエラーとなります。このようなエラーは文法エラー(syntax error) と呼ばれ,Jupyter Notebook では,以下のように誤りやその箇所を指摘してくれます。

In [3]:
s1 = 100000;
s2 = 40000;
x1 = s2 × 100 ÷ s1;
disp(x1)
error: parse error:

  syntax error

>>> x1 = s2 × 100 ÷ s1;
            ^
40

別の種類の誤りもあります。たとえば,「100」を「1000」と入力した場合...

In [4]:
s1 = 100000;
s2 = 40000;
x1 = s2*1000/s1;
disp(x1)
400

上記の例では syntax error (文法エラー) の警告が出ませんが,答えは明らかに変です。(エンゲル係数の最大値は100だから。)

このような場合はコンピュータにとって誤りではなく,指示通りに計算し,その結果を表示します。 このようなプログラム作成者の不注意や勘違いによって生じるエラーには注意が必要です。

プログラムが正しく動作しない場合は,誤りの箇所を探し,修正して再度動作を確認します。正しく動作するようになるまで,プログラムを修正し,実行(Shift + Enter)します。この作業をデバグといいます。

データの型と変数

上記の例の 1行目では,s1 という名前の変数に 100000 という 数値を代入しています。変数名はアルファベットで始まる英数字列にします。大文字と小文字は区別されます。

変数 ss に文字列を代入する場合は,以下のように "(または ')で囲みます。

In [5]:
ss = "エンゲル係数";
disp(ss)
エンゲル係数

式と演算

掛け算を示す演算子は *,割り算を示す演算子は /。足し算は +,引き算は - です。

文字列の連結演算には strcat()cstrcat() を使います。以下の例を参照。(strcat() は末尾の空白を削除します。)

In [6]:
x0 = "エンゲル ";
y0 = "係数";
z0 = strcat(x0, y0);
z1 = cstrcat(x0, y0);
disp(z0)
disp(z1)
エンゲル係数
エンゲル 係数

文字列と数値を直接連結することはできません。

In [7]:
s1 = 100000;
s2 = 40000;
x1 = s2*100/s1;
z1 = cstrcat("エンゲル係数は ", x1);
disp(z1)
error: cstrcat: arguments must be character strings
error: called from
    cstrcat at line 60 column 5
エンゲル 係数

そんなときは,num2str() 関数で数値を文字列に変換して連結します。

In [8]:
s1 = 100000;
s2 = 40000;
x1 = s2*100/s1;
z1 = cstrcat("エンゲル係数は ", num2str(x1));
disp(z1)
エンゲル係数は 40

別解として,以下のように printf() を利用して文字列と数値を並べて表示することもできます。

In [9]:
s1 = 100000;
s2 = 40000;
x1 = s2*100/s1;

printf("エンゲル係数は "), disp(x1)
エンゲル係数は 40

printf() は明示的に \n で改行を指示しないかぎり改行しません。一方,disp() は表示後,改行します。

以下は,書式設定の例です。%6.2f は表示スペースを6桁分とり,小数点以下を2桁で表示します。

In [10]:
printf("エンゲル係数は %6.2f です。\n", x1)
エンゲル係数は  40.00 です。

主な演算子

加減乗除の演算子 +, -, *, /

優先的に計算したい箇所は丸括弧 ( )で グループ化します。

In [11]:
2 + (50 - 5 * 6)/4
ans = 7

除算 / は常に浮動小数点数を返します。 既定(format short)では5桁表示ですが,fomat long g などと指定すると15桁表示します。

In [12]:
17 / 3
ans = 5.6667
In [13]:
format long g
17 / 3
ans = 5.666666666666667

累乗は ** または ^ です。

In [14]:
format short
2^4
2**4
ans = 16
ans = 16

対話型プログラム

上記の例では,プログラムの中で消費支出や飲食費を決めていましたが,今度はプログラムを実行する人が入力できるようにしてみます。

input() 関数は入力された値を返します。

In [15]:
s1 = input("消費支出? ");
s2 = input("飲食費? ");
x1 = s2*100/s1;
z1 = cstrcat("エンゲル係数は ", num2str(x1), " です。");
disp(z1)
消費支出?100000
飲食費?40000
エンゲル係数は 40 です。

関数の定義と呼び出し

(これまでの例では1回しか計算していませんが)よく使う計算を「関数」として定義する例です。

以下の例では,1行目から3行目でエンゲル係数を計算する関数 calc() を定義し,7行目でその関数を呼び出しています。

% から始まる行はコメント(実行に影響しない注釈)です。

In [16]:
function x = calc(s1, s2)
    % 消費支出 s1 円と飲食費 s2 円からエンゲル係数を計算する。
    x = s2*100/s1;
endfunction

format short
s1 = input("消費支出? ");
s2 = input("飲食費? ");
x1 = calc(s1, s2);
z1 = cstrcat("エンゲル係数は ", num2str(x1), " です。");
disp(z1)
消費支出?120000
飲食費?43000
エンゲル係数は 35.8333 です。

いったん定義された関数は,以下のように何回でも利用できます。

In [17]:
calc(120000, 53000)
ans = 44.167

(練習)

消費支出を入力すると,エンゲル係数が 20 ~ 40 となる飲食費を表示するプログラムをつくりなさい。

数学関数を使う

以下の例では,4行目で x 以下の最大の整数を与える関数 floor(x) を使い,小数点以下を切り捨てたエンゲル係数の値を表示させています。(ちなみに,x 以上の最小の整数を与える関数は ceil(x) です。)

また,エンゲル係数を計算する部分はすでに関数 calc() として定義していますが,プログラム全体を関数 engel() として定義してしまいます。

In [18]:
function engel()
    s1 = input("消費支出? ");
    s2 = input("飲食費? ");
    x1 = floor(calc(s1, s2));
    z1 = cstrcat("エンゲル係数は ", num2str(x1), 
                 " です。(小数点以下切り捨て)");
    disp(z1)
endfunction
In [19]:
engel()
消費支出?120000
飲食費?43000
エンゲル係数は 35 です。(小数点以下切り捨て)

(練習)

小数点以下を切り上げたエンゲル係数の値を表示させるように上記のプログラムを変更しなさい。

参考:数値の丸めと四捨五入

Octave の組み込み関数に round()roundb() があります。どちらも数値を丸める関数です。

以下の例からわかるように,小数点以下が 0.5 未満の場合と 0.5 を超える場合は,どちらも 四捨五入 と同様の丸め方ですが,ちょうど 0.5 のときは,round() はいわゆる 四捨五入 で切り上げます。

一方,roundb() の場合は,偶数に丸められていることがわかります。 これを偶数への丸めといい,端数が 0.5 より小さいなら切り捨て,端数が0.5 より大きいなら切り上げ,端数がちょうど 0.5 なら切り捨てと切り上げのうち結果が偶数となる方へ丸めます。

In [20]:
M1 = [12.49  12.50  12.51];
M2 = [13.49  13.50  13.51];
[round(M1), round(M2)]
[roundb(M1), roundb(M2)]
ans =

   12   13   13   13   14   14

ans =

   12   12   13   13   14   14

条件分岐

条件分岐とは,例えばエンゲル係数の値によって実行文を変えることです。例を示します。

関数 engel() を以下のように変更します。

In [21]:
function engel()
    s1 = input("消費支出? ");
    s2 = input("飲食費? ");
    x1 = calc(s1, s2);
    if (x1 >= 80) 
        z1 = cstrcat("エンゲル係数は ", num2str(x1), 
                     " です。飲食費を使いすぎです。");
    else
        z1 = cstrcat("エンゲル係数は ", num2str(x1), " です。");        
    endif
    disp(z1)
endfunction
In [22]:
engel()
消費支出?100000
飲食費?80000
エンゲル係数は 80 です。飲食費を使いすぎです。
In [23]:
engel()
消費支出?100000
飲食費?40000
エンゲル係数は 40 です。

条件分岐の if 文の書式は以下のとおりです。

if (条件式1)
    条件式1 が満たされた場合に実行する文
elseif (条件式2)
    条件式2 が満たされた場合に実行する文
else
    それ以外の場合に実行する文
endif

(練習)

上記の条件分岐を,エンゲル係数が

  1. 80 以上の場合には「飲食費を使いすぎです。」
  2. 80 未満 40 以上の場合には「正常です。」
  3. 40 未満の場合には「もっと食べましょう。」

と表示させるように変更しなさい。

繰り返し処理

while

以下の例では,1行目で代入された消費支出に対してエンゲル係数を計算します。

1回だけ計算して表示するのではなく,2行目の飲食費の初期値(ここでは 40000 円)から,5行目の条件式(ここでは 80000 円以下)が成り立つ場合に,8行目にあるように 5000 円ずつ増やしながらエンゲル係数を表示します。

In [24]:
s1 = 100000;
s2 = 40000;
z0 = cstrcat("消費支出 ", num2str(s1), 
             " 円に対するエンゲル係数の値");
disp(z0)

while (s2 <= 80000)
    x1 = calc(s1, s2);
    z1 = cstrcat("   飲食費 ", num2str(s2), 
                 " 円の場合: ", num2str(x1));
    disp(z1)
    s2 = s2 + 5000;
endwhile
消費支出 100000 円に対するエンゲル係数の値
   飲食費 40000 円の場合: 40
   飲食費 45000 円の場合: 45
   飲食費 50000 円の場合: 50
   飲食費 55000 円の場合: 55
   飲食費 60000 円の場合: 60
   飲食費 65000 円の場合: 65
   飲食費 70000 円の場合: 70
   飲食費 75000 円の場合: 75
   飲食費 80000 円の場合: 80

while 文の書式は以下の通りです。

while (条件式)
    条件式が成り立つ場合に実行する式
endwhile

for

同様の繰り返し処理を for 文を使って行うこともできます。

In [25]:
s1 = 100000;
z0 = cstrcat("消費支出 ", num2str(s1), 
             " 円に対するエンゲル係数の値");
disp(z0)

for s2 = 40000:5000:80000
    x1 = calc(s1, s2);
    z1 = cstrcat("   飲食費 ", num2str(s2), 
                 " 円の場合: ", num2str(x1));
    disp(z1)
endfor
消費支出 100000 円に対するエンゲル係数の値
   飲食費 40000 円の場合: 40
   飲食費 45000 円の場合: 45
   飲食費 50000 円の場合: 50
   飲食費 55000 円の場合: 55
   飲食費 60000 円の場合: 60
   飲食費 65000 円の場合: 65
   飲食費 70000 円の場合: 70
   飲食費 75000 円の場合: 75
   飲食費 80000 円の場合: 80

for 文の書式は以下のとおりです。

for i = 開始値:増分:終端値
    i が開始値以上,終端値以下の場合に実行される文
endfor

(練習)

以下の値を求めるプログラムを作成せよ。

$$\sum_{n = 1}^{10} n^2$$

参考:要素毎の演算

上記の(練習)は,while または for を使った繰り返しの練習を想定しるが,Octave ではベクトルの要素毎の演算と sum() を使って以下のように求めることができます。

In [26]:
x = [1:10]       % 1 から 10 までの数値を成分にもつベクトル
x.**2            % ピリオド . をつけると要素毎に2乗する
sum([1:10].**2)  % sum() は要素の和をとる
x =

    1    2    3    4    5    6    7    8    9   10

ans =

     1     4     9    16    25    36    49    64    81   100

ans = 385