Return to 参考:Maxima 編アーカイブ

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

あちらの「はじめての Maxima プログラミング」に若干の補足説明を追加したもの。Maxima には構造化プログラミングの機能もあり,条件分岐や繰り返し処理などを利用することで,プログラミング言語としても利用できます。

エンゲル係数を求める例

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

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

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

Maxima の文末は,;$ で終わります。$ で終わる場合は結果が表示されません。

In [1]:
37000/110000 * 100;
Out[1]:
\[\tag{${\it \%o}_{1}$}\frac{370}{11}\]

Maxima では割り算の結果は厳密な有理数として出力されます。(近似的な)実数(小数点数)で表示させたい場合は,とりあえずは数値のどれか1つにでも小数点をつけます。(あとで float() 関数で実数に変換することも説明します。)

In [2]:
37000/110000 * 100.0;
Out[2]:
\[\tag{${\it \%o}_{2}$}33.63636363636363\]

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

以下の例では,

  1. /**/ で囲まれる部分はコメント。コメントはそれを読む人間のための注釈であり,プログラムの実行時には無視されます。
  2. 3行目で変数 spending に消費支出である 110000(円)という数値を代入し,
  3. 4行目で変数 food に飲食費である 37000(円)という数値を代入し,
  4. 5行目でエンゲル係数を計算して,変数 eng に代入し,表示させています。

Maxima では,変数への代入は : です。

In [3]:
/* エンゲル係数を求める
    消費支出 spending, 飲食費 food, エンゲル係数 eng */
spending: 110000$
food: 37000$
eng: food / spending * 100.0;
Out[3]:
\[\tag{${\it \%o}_{5}$}33.63636363636363\]

エラーとデバグ

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

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

In [4]:
spending: 110000$
food: 37000$
eng: food x 100 / spending;
incorrect syntax: x is not an infix operator
eng: food x 
          ^

   [Condition of type MAXIMA-JUPYTER::MAXIMA-SYNTAX-ERROR]

#<ENVIRONMENT {1003C36F13}>
   [Environment of thread #<THREAD "SHELL Thread" RUNNING {1004148003}>]

...

別の種類の誤りもあります。たとえば,パーセントにするためにかける「100.0」を誤って「1000.0」と入力した場合…

In [5]:
spending: 110000$
food: 37000$
eng: food / spending * 1000.0;
Out[5]:
\[\tag{${\it \%o}_{10}$}336.3636363636364\]

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

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

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

データの型と変数

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

データ型には数値(整数・有理数・浮動小数点数)と文字列などがあります。

変数 moji に文字列を代入する場合は,以下のように ” で囲みます。

In [6]:
moji: "エンゲル係数"$
print(moji)$
エンゲル係数

式と演算

主な演算子

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

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

In [7]:
2 +  50 - 5 * 6 /2;
2 + (50 - 5 * 6)/2;
Out[7]:
\[\tag{${\it \%o}_{13}$}37\]
Out[7]:
\[\tag{${\it \%o}_{14}$}12\]

文字列の連結

文字列の連結演算は concat() を使います。以下の例を参照。

In [8]:
mo: "エンゲル"$
ji: "係数"$
concat(mo, ji);
Out[8]:
\[\tag{${\it \%o}_{17}$}\mbox{ エンゲル係数 }\]

文字列と数値を直接連結するときも,concat() を使います。

In [9]:
eng: 37000 / 110000 * 100.0$
output: concat("エンゲル係数は ", eng, " です。")$
print(output)$
エンゲル係数は 33.63636363636363 です。

文字列と連結しなくても,以下のようにも書くことができます。
print() 文は,カンマ (,) で区切ることによって複数の項目(変数)を表示することができます。

In [10]:
print("エンゲル係数は ", eng,  " です。")$
エンゲル係数は \(33.63636363636363\) です。

参考:整数を3桁0詰めで表示する例

いわゆる %03d のように,整数を3桁,0詰めで表示する需要があるかもしれません。後述する書式指定でできそうにないので,以下のような奇策を考えてみました。

  1. concat(i) で整数 i を文字列にし,
  2. substring( , 2) で2文字目からの文字列にして print()
In [11]:
for i:1000 thru 1003 do(
  print(substring(concat(i), 2))
)$
000
001
002
003

割り算の商と余り

Maxima の割り算は以下のように厳密な値(有理数のまま)を返します。

In [12]:
17/3;
34/6; /* 約分してくれる */
Out[12]:
\[\tag{${\it \%o}_{23}$}\frac{17}{3}\]
Out[12]:
\[\tag{${\it \%o}_{24}$}\frac{17}{3}\]

割り算の「商」と「余り」を求める場合は,quotient() 関数や mod() 関数を使います。

In [13]:
/* 商 */
quotient(17, 3);
/* 余り */
mod(17, 3);

/* 検算 */
quotient(17, 3) * 3 + mod(17, 3);
Out[13]:
\[\tag{${\it \%o}_{25}$}5\]
Out[13]:
\[\tag{${\it \%o}_{26}$}2\]
Out[13]:
\[\tag{${\it \%o}_{27}$}17\]
In [14]:
/* 別途述べる,切り捨てする関数 floor() を使う例 */
/* 商 */
floor(17/3);
/* 余り */
17 - floor(17/3) * 3;
Out[14]:
\[\tag{${\it \%o}_{28}$}5\]
Out[14]:
\[\tag{${\it \%o}_{29}$}2\]

浮動小数点数表示する場合は,すでに述べたように数値に小数点をつけて整数でないことをアピールするか,浮動小数点数に変換する関数 float() を使います。

In [15]:
17.0/3;
float(17/3);
Out[15]:
\[\tag{${\it \%o}_{30}$}5.666666666666667\]
Out[15]:
\[\tag{${\it \%o}_{31}$}5.666666666666667\]

べき乗は **(または ^)です。以下の例では $2^4 = 2 \times 2 \times 2 \times 2$ と $2^{1/2} = \sqrt{2}$ の値を表示します。

In [16]:
2**4;
2**(1/2);
float(%);
Out[16]:
\[\tag{${\it \%o}_{32}$}16\]
Out[16]:
\[\tag{${\it \%o}_{33}$}\sqrt{2}\]
Out[16]:
\[\tag{${\it \%o}_{34}$}1.414213562373095\]

練習 1

1 天文単位(1 au)の距離を光速 c で進むのに要する時間は何分何秒か。ただし,

  • 1 au は 12 桁の定義定数(誤差無し)で 149597870700 m
  • c は 9 桁の定義定数(誤差無し)で 299792458 m/s である。

参考:

In [ ]:

対話型プログラム

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

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

注意: Maxima では read() に対して値をキーボードから入力する場合も,最後は ; をつけてから Enter キーを押します。

In [17]:
spending: read("消費支出? (数値の後に ; をつけて Enter キー)")$
food: read("飲食費? (数値の後に ; をつけて Enter キー)")$
eng: float(food / spending * 100)$ /* float() で変換 */
print("エンゲル係数は ", eng, " です。")$
消費支出? (数値の後に ; をつけて Enter キー)
110000;
飲食費? (数値の後に ; をつけて Enter キー)
37000;
エンゲル係数は \(33.63636363636363\) です。

関数の定義と呼び出し

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

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

忘れないで!: Maxima では read() に対して値をキーボードから入力する場合も,最後は ; をつけてから Enter キーを押します。

In [18]:
calc(spending, food):= float(food / spending * 100)$
In [19]:
spending: read("消費支出? ")$
food: read("飲食費? ")$

/* 上で定義した関数 calc() を使う */
eng: calc(spending, food)$
print("エンゲル係数は ", eng, " です。")$
消費支出?
110000;
飲食費?
37000;
エンゲル係数は \(33.63636363636363\) です。

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

In [ ]:
calc(120000, 53000);

練習 2

消費支出を入力すると,エンゲル係数が 20 ~ 40 となる飲食費を表示するプログラムをつくりなさい。(以下の例はエンゲル係数が 20 以上となる飲食費を表示します。編集して完成させなさい。)

In [ ]:
spending: read("消費支出? ")$
food20: spending * 0.2$

print("エンゲル係数が 20 ~ 40 となる飲食費は...",
      food20, "円〜")$
In [ ]:

定義定数や数学関数を使う

Maxima には,$\sin x, \cos x, \tan x$などの数学関数が組み込まれています。また,数学定数としては円周率 %pi,虚数単位 %i,ネイピア数 %e,無限大 inf などが使えます。

In [20]:
/* 円周率 π */
%pi;
sin(%pi/2);
cos(%pi/3);
tan(%pi/4);
Out[20]:
\[\tag{${\it \%o}_{44}$}\pi\]
Out[20]:
\[\tag{${\it \%o}_{45}$}1\]
Out[20]:
\[\tag{${\it \%o}_{46}$}\frac{1}{2}\]
Out[20]:
\[\tag{${\it \%o}_{47}$}1\]
$$ i \equiv \sqrt{-1}, \quad\therefore\ \ i^2 = -1$$
In [21]:
/* 虚数単位の2乗は... */
%i**2;
Out[21]:
\[\tag{${\it \%o}_{48}$}-1\]
$$\log_e e = 1$$
In [22]:
/* log は自然対数 */
log(%e);
Out[22]:
\[\tag{${\it \%o}_{49}$}1\]

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

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

以下の例のように,block() は複数の関数や式を ,(カンマ)で区切って記述します。

In [23]:
engel():= block(
  spending: read("消費支出? "),
  food: read("飲食費? "),
  
  /* 上で定義された calc() を使用 */
  eng: calc(spending, food),
  print("エンゲル係数は ", floor(eng), 
        " です。(小数点以下切り捨て)")
)$
In [24]:
engel()$
消費支出?
110000;
飲食費?
37000;
エンゲル係数は \(33\) です。(小数点以下切り捨て)

Maxima で使える数学関数

Maxima で使える数学関数や数学定数については,以下のマニュアルのページを参照してください。

練習 3

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

ヒント: Maxima 5.42.2 Manual: 10. Mathematical Functionsceiling() を参照。

In [ ]:

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

Maxima の組み込み関数に round() があります。数値を丸める関数ですが,いわゆる四捨五入とはちょっと違うようです。

以下の例からわかるように,小数点以下が 0.5 未満の場合と 0.5超える場合は四捨五入と同様の丸め方ですが,ちょうど 0.5 のときは,偶数に丸められていることがわかります。

これを偶数への丸めといい,端数が 0.5 より小さいなら切り捨て,端数が0.5 より大きいなら切り上げ,端数がちょうど 0.5 なら切り捨てと切り上げのうち結果が偶数となる方へ丸めます。

In [25]:
print(round(12.49), "  ", round(12.50), "  ", round(12.51))$
\(12\) \(12\) \(13\)
In [26]:
print(round(13.49), "  ", round(13.50), "  ", round(13.51))$
\(13\) \(14\) \(14\)

条件分岐

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

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

In [27]:
/* 忘れてるかもしれないのであらためて定義しておく */
calc(spending, food):= float(food / spending * 100)$

engel():= block(
  spending : read("消費支出? "),
  food : read("飲食費? "),

/* 上で定義した関数 calc() を使う */
  eng : calc(spending, food),
  if eng >= 40 then 
      print("エンゲル係数は ", round(eng), 
            " です。高い値です。")
  else
      print("エンゲル係数は ", round(eng), " です。")
)$
In [28]:
engel()$
消費支出?
10000;
飲食費?
4000;
エンゲル係数は \(40\) です。高い値です。
In [29]:
engel()$
消費支出?
10000;
飲食費?
3000;
エンゲル係数は \(30\) です。

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

if 条件式1 then
    条件式1が満たされる場合に実行する文
  elseif 条件式2 then
    条件式2が満たされる場合に実行する文
  else
    上記以外の場合に実行する文 $

関係演算子

上の例では,eng >= 40 つまり eng40 以上のとき,という関係演算子を使いました。他の例は以下のとおりです。

a < b  ab より小さい
a <= b  ab 以下
a > b  ab より大きい
a >= b  ab 以上

is(equal(a, b))ab が等しいとき true, そうでないとき false を返します。equal(a, b) の否定は notequal(a, b) です。

練習 4

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

  1. 40 以上の場合には「高い値です。」
  2. 20 未満の場合には「低い値です。」

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

In [ ]:

繰り返し処理

while

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

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

In [30]:
spending : 110000 $
food : 30000 $
print("消費支出", spending, "円に対するエンゲル係数の値")$

while food <= 50000 do(
    eng : calc(spending, food),
    print("   飲食費", food, "円の場合: ", eng),
    food : food + 5000
)$
消費支出 \(110000\) 円に対するエンゲル係数の値
飲食費 \(30000\) 円の場合: \(27.27272727272727\)
飲食費 \(35000\) 円の場合: \(31.81818181818182\)
飲食費 \(40000\) 円の場合: \(36.36363636363637\)
飲食費 \(45000\) 円の場合: \(40.90909090909091\)
飲食費 \(50000\) 円の場合: \(45.45454545454545\)

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

while 条件式 do(
    条件式が成り立つ場合に実行する式1, 
    条件式が成り立つ場合に実行する式2, ... ,
    条件式が成り立つ場合に実行する式
)

for

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

In [31]:
spending: 110000 $
food: 30000 $
print("消費支出", spending, "円に対するエンゲル係数の値")$

for i: 30000 thru 50000 step 5000 do(
  eng: calc(spending, food),
  print("   飲食費", food, "円の場合: ", eng),
  food: food + 5000
)$
消費支出 \(110000\) 円に対するエンゲル係数の値
飲食費 \(30000\) 円の場合: \(27.27272727272727\)
飲食費 \(35000\) 円の場合: \(31.81818181818182\)
飲食費 \(40000\) 円の場合: \(36.36363636363637\)
飲食費 \(45000\) 円の場合: \(40.90909090909091\)
飲食費 \(50000\) 円の場合: \(45.45454545454545\)

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

for ivar: istart thru iend step istep
  do(
    ivar が istart から iend の間,実行される文
  )$

ivar は整数変数で,その初期値は istart。文が実行されるごとに,ivar の値は istep だけ加算され,iend の値になるとループが終了します。

step の部分を省略すると,1 ずつ加算されます。

例:数列の和

$\displaystyle \sum_{n = 1}^{10} n$ を繰り返し処理の練習として求めてみます。

In [32]:
/* Maxima の関数 sum() を使えばこの通りだが... */
sum(n, n, 1, 10);
Out[32]:
\[\tag{${\it \%o}_{66}$}55\]
In [33]:
/* 一般の n までの和を求める例 */
nusum(i, i, 1, n);
Out[33]:
\[\tag{${\it \%o}_{67}$}\frac{n\,\left(n+1\right)}{2}\]
In [34]:
/* 繰り返し処理 while の練習として */
answer: 0$
i: 1$
while i <= 10 do(
  answer: answer + i, 
  i: i + 1
)$
answer;
Out[34]:
\[\tag{${\it \%o}_{71}$}55\]
In [35]:
/* 繰り返し処理 for の練習として */
answer: 0$
for i: 1 thru 10 do(
  answer: answer + i
)$
answer;
Out[35]:
\[\tag{${\it \%o}_{74}$}55\]

参考:再帰的定義関数と繰り返し処理

参考までに,再帰的定義関数を使った方法について。

$i=1$ から $i = n$ までの $i$ の和 $\displaystyle \sum_{i=1}^n i$ を求める関数 mysum(n) を以下のように定義する。

\begin{eqnarray}
\mbox{mysum}(1) &=& 1 \\
\mbox{mysum}(2) &=& 1 + 2 = \mbox{mysum}(1) + 2 \\
\mbox{mysum}(3) &=& 1 + 2 + 3 = \mbox{mysum}(2) + 3 \\
&\vdots& \\
\mbox{mysum}(n) &=& \mbox{mysum}(n-1) + n
\end{eqnarray}

In [36]:
/* 自分自身の一つ前の値を使って自分自身を定義する */
/* このように定義された関数を再帰的定義関数という */

mysum(n):= if n = 1 then 1 else mysum(n-1) + n$

mysum(10);
Out[36]:
\[\tag{${\it \%o}_{76}$}55\]

練習 5

以下の値を求める関数mysum3() として作成し,$n = 10$ および $n = 100$ の場合を出力せよ。

$$\sum_{i = 1}^{n} i^3$$

In [ ]:
/* Maxima の関数を使うと... */
sum(i**3, i, 1, 10);
sum(i**3, i, 1, 100);
/* だけど,自分で作ってみるんですよ */

リストとファイルの読み書き

リスト

要素が5個のリストの各成分に数値を入れたり表示したりする例です。

In [37]:
a: [5, 4, 3, 2, 1]$
print("全成分を一挙に表示")$
print(a)$
全成分を一挙に表示
\(\left[ 5 , 4 , 3 , 2 , 1 \right] \)

少し表示を工夫してみます。sprint() 関数は(改行せずに)横に続けて出力します。

for c in a do()$

はリスト a の要素を c として順番にとります。

In [38]:
print("全成分を横に表示")$
for i in a do(sprint(i))$

print("全成分を横に表示")$
for i in a do(print(i))$
全成分を横に表示
5 4 3 2 1
全成分を横に表示
\(5\)
\(4\)
\(3\)
\(2\)
\(1\)

5個の成分を全てゼロにしてリストを初期化する例です。

In [39]:
/* リスト b の5つの要素を全て 0 に*/
b : makelist(0, 5)$
print(b)$
/* 3番目の要素の値を設定 */
b[3] : 3$
print(b)$
\(\left[ 0 , 0 , 0 , 0 , 0 \right] \)
\(\left[ 0 , 0 , 3 , 0 , 0 \right] \)

ファイルからリストへの読み込み

以下のようなテキストファイル mydata.txt があるとします。ない場合は「新規」の「テキストファイル」で作成します。

$ cat mydata.txt
1
2
3
4
5
6

ファイル mydata.txt の数値をリスト a に読み込みます。

In [40]:
a: read_list("mydata.txt");
Out[40]:
\[\tag{${\it \%o}_{88}$}\left[ 1 , 2 , 3 , 4 , 5 , 6 \right] \]
In [41]:
/* 要素の総和,平均,最大値,最小値を出力します。*/
print("要素の総和は", lsum(i, i, a))$
print("要素の平均は", float(lsum(i, i, a)/length(a)))$
print("最大値は    ", lmax(a))$
print("最小値は    ", lmin(a))$
要素の総和は \(21\)
要素の平均は \(3.5\)
最大値は \(6\)
最小値は \(1\)

次は,以下のような「6行2列」のデータが入ったテキストファイル mydata2.txt を読み込む例です。

$ cat mydata2.txt
1  1
2  4
3  9
4 16
5 25
6 36
In [42]:
b: read_nested_list("mydata2.txt")$
print(b)$
\(\left[ \left[ 1 , 1 \right] , \left[ 2 , 4 \right] , \left[ 3 , 9 \right] , \left[ 4 , 16 \right] , \left[ 5 , 25 \right] , \left[ 6 , 36 \right] \right] \)
In [43]:
/* 入れ子になっているリストの成分を取り出す方法 */
b[2];
b[2][2];
Out[43]:
\[\tag{${\it \%o}_{95}$}\left[ 2 , 4 \right] \]
Out[43]:
\[\tag{${\it \%o}_{96}$}4\]

リストではなく,行列として読み込む場合は以下のようにします。

In [44]:
M: read_matrix("mydata2.txt");
Out[44]:
\[\tag{${\it \%o}_{97}$}\begin{pmatrix}1 & 1 \\ 2 & 4 \\ 3 & 9 \\ 4 & 16 \\ 5 & 25 \\ 6 & 36 \\ \end{pmatrix}\]
In [45]:
/* 行列として読み込んだ場合の成分の取り出し方 */
M[2, 2];
Out[45]:
\[\tag{${\it \%o}_{98}$}4\]

ファイルへの書き込み

ファイルへの書き込み例です。この例では,$𝑥$ と $\sin 𝑥$ の値を $𝑥=0.1$ から $𝑥=1.0$ まで書き込んでいます。

In [46]:
x: makelist([0.1*i, sin(0.1*i)], i, 1, 10)$
write_data(x, "myoutput.txt")$

JupyterHub のホームに「myoutput.txt」が作成されていることを確認します。ファイル名をクリックすると,内容を確認できます。

0.1 0.09983341664682815
0.2 0.1986693307950612
0.3 0.2955202066613396
0.4 0.3894183423086505
0.5 0.479425538604203
0.6000000000000001 0.5646424733950355
0.7000000000000001 0.6442176872376911
0.8 0.7173560908995228
0.9 0.7833269096274834
1.0 0.8414709848078965

書式を指定してファイルへ書き込み

上記の write_data() では数値の桁数に凸凹があります。後で説明する printf() を使って書式設定してファイルへ書き込む例です。

In [47]:
/* 書き込み用に myoutput2.txt をオープンする */
s: openw("myoutput2.txt")$

for i:1 thru 10 do(
  /* 小数点以下3桁  小数点以下15桁 */
  printf(s, "~,3f   ~,15f~%", 0.1*i, sin(0.1*i))
)$

/* 書き込みが終わったらクローズする */
close(s)$

JupyterHub のホームに「myoutput2.txt」が作成されていることを確認します。ファイル名をクリックすると,内容を確認できます。

0.100   0.099833416646828
0.200   0.198669330795061
0.300   0.295520206661340
0.400   0.389418342308651
0.500   0.479425538604203
0.600   0.564642473395035
0.700   0.644217687237691
0.800   0.717356090899523
0.900   0.783326909627483
1.000   0.841470984807897

readline() でファイルから1行ずつ読み込み

以下のようにして,ファイルから1行ずつ読み込んで表示させることができます。ファイルの最後まで readline() で1行ずつ読み込み,読み込む文字列がなくなったら終了します。

In [48]:
/* 読み込み用に myoutput2.txt をオープンする */
s: openr("myoutput2.txt")$

/* ファイルの最後まで1行ずつ読み込み表示する */
while stringp(tmp:readline(s)) do print(tmp)$

/* 読み込みが終わったらクローズする */
close(s)$
0.100 0.099833416646828
0.200 0.198669330795061
0.300 0.295520206661340
0.400 0.389418342308651
0.500 0.479425538604203
0.600 0.564642473395035
0.700 0.644217687237691
0.800 0.717356090899523
0.900 0.783326909627483
1.000 0.841470984807897

リスト・行列の相互変換

行列からリストへの変換は args() 関数で行います。

In [49]:
M;
m: args(M);
Out[49]:
\[\tag{${\it \%o}_{107}$}\begin{pmatrix}1 & 1 \\ 2 & 4 \\ 3 & 9 \\ 4 & 16 \\ 5 & 25 \\ 6 & 36 \\ \end{pmatrix}\]
Out[49]:
\[\tag{${\it \%o}_{108}$}\left[ \left[ 1 , 1 \right] , \left[ 2 , 4 \right] , \left[ 3 , 9 \right] , \left[ 4 , 16 \right] , \left[ 5 , 25 \right] , \left[ 6 , 36 \right] \right] \]

また,リストから行列への変換は以下のようにします。

In [50]:
b;
M1: apply('matrix, b);
Out[50]:
\[\tag{${\it \%o}_{109}$}\left[ \left[ 1 , 1 \right] , \left[ 2 , 4 \right] , \left[ 3 , 9 \right] , \left[ 4 , 16 \right] , \left[ 5 , 25 \right] , \left[ 6 , 36 \right] \right] \]
Out[50]:
\[\tag{${\it \%o}_{110}$}\begin{pmatrix}1 & 1 \\ 2 & 4 \\ 3 & 9 \\ 4 & 16 \\ 5 & 25 \\ 6 & 36 \\ \end{pmatrix}\]

書式指定

浮動小数点数の書式指定 "~f"

実数を表示する際,全体の文字数や小数点以下の桁数を指定して表示させる例です。

例えば "~8,5f" は全体で 8 桁の表示範囲をとり,小数点以下は 5 桁表示します。"~%" は改行を表します。

(小数点が , になっているのはなんとなく… と思って調べると,Wikipedia 日本語版では . 「ピリオド」(イギリス式)で , 「コンマ」(フランス式)とある。フランスだけでなく非英語圏では「コンマ」がよく使われるらしい。)

In [51]:
sqrtwo : float(sqrt(2))$
printf(true, "12345678901234567890    桁数~%")$
printf(true, "~f~%", sqrtwo)$
printf(true, "~8,5f~%", sqrtwo)$
printf(true, "~18,15f~%", sqrtwo)$
printf(true, "2の平方根は ~18,15f です~%", sqrtwo)$
12345678901234567890    桁数
1.4142135623730951
 1.41421
 1.414213562373095
2の平方根は  1.414213562373095 です

全体の桁数のみを指定することもできます。例えば,"~7f" のように全体の桁数 7 桁のみを指定します。また,小数点以下の桁数のみを"~,2f" のように指定することもできます。

In [52]:
printf(true, "12345678901234567890    桁数~%")$
printf(true, "~7f~%", sqrtwo)$
printf(true, "~,2f~%", sqrtwo)$
12345678901234567890    桁数
1.41421
1.41

fpprintprec による表示桁数設定

また,以上のように個別に指定しなくても,

fpprintprec: 5$

のようにすると,その後の小数表示の桁数が(この場合であれば)5桁になります。表示桁数のみの変更であり,計算の精度等には影響しません。既定に戻すには

fpprintprec: 0$
In [53]:
fpprintprec: 5$
sqrtwo;
Out[53]:
\[\tag{${\it \%o}_{121}$}1.4142\]
In [54]:
fpprintprec: 0$
sqrtwo;
Out[54]:
\[\tag{${\it \%o}_{123}$}1.414213562373095\]

練習 6

以下の出力を,エンゲル係数の値を(四捨五入して)小数点以下1桁まで表示するように変更しなさい。

In [ ]:
spending: 110000$
food: 37000$
eng: float(food * 100 / spending)$ 
print("エンゲル係数は ", eng, " です。")$

指数表式の指定 "~e"

非常に大きい数や小さい数を表示するときには, $1.9891 \times 10^{30}$
や $6.67430 \times 10^{-11}$
などのような指数表示をします。

例えば "~9,2e" では,全体で 9 桁の表示分を確保し,小数点以下は 2 桁表示します。

In [55]:
c: 299792458$
printf(true, "12345678901234567890    桁数~%")$
printf(true, "~9,2e~%", c)$
printf(true, "~15,8e~%", c)$
printf(true, "光速は ~15,8e m/s です。~%", c)$
12345678901234567890    桁数
  3.00e+8
  2.99792458e+8
光速は   2.99792458e+8 m/s です。

全体の桁数のみを指定したり,小数点以下の桁数のみを指定することもできます。

In [56]:
printf(true, "12345678901234567890    桁数~%")$
printf(true, "~,2e~%", c)$
printf(true, "~15e~%", c)$
12345678901234567890    桁数
3.00e+8
  2.99792458e+8

"~f" または "~e" となる "~g"

"~g" は数値の大きさによって,"~f" または "~e" に(だいたい)なります。

In [57]:
printf(true, "~,2g~%", sqrtwo)$ /* 総桁数が 2 になる? */
printf(true, "~,2f~%", sqrtwo)$

printf(true, "~,2g~%", c)$
printf(true, "~,2e~%", c)$
1.4    
1.41
3.00e+8
3.00e+8

文字型の書式指定と substring()

文字列の書式指定は "~a" です。"~a" では文字数の設定ができないようなので,以下のように substring() を使ってみます。

substring() で文字列の一部を取り出します。

substring(str, i, j) は文字列 stri 番目から始まり,j 番目以降を切り取った(つまり,i 番目から j-1 番目までの)文字列を与えます。

In [58]:
ten: "1234567890"$
printf(true, "~a ~a~%", substring(ten, 1, 6), 
                        substring(ten, 1, 6))$
printf(true, "~a ~a~%", substring(ten, 1, 7), 
                        substring(ten, 1, 5))$
12345 12345
123456 1234

整数の書式指定

整数の書式指定は "~d" です。"~5d" のように桁数を指定することもできます。

In [59]:
v: [1, 23, 456, 7890]$

/* v の各要素を整数5桁右詰めで表示 */
for c in v do printf(true, "~5d~%", c)$
    1
   23
  456
 7890

参考:整数を 0 詰めで表示する策

いわゆる %05d のように 0 詰めで表示する機能はないようなので,苦肉の策:

In [60]:
/* v の各要素を整数5桁 0 詰めで表示する */
for c in v do(
  printf(true, "~a~%", substring(concat(c+100000), 2))
)$
00001
00023
00456
07890
In [61]:
m: [[1, 0, 0, 0], 
    [0, 23, 0, 0], 
    [0, 0, 456, 0], 
    [0, 0, 0, 7890]]$
In [62]:
/* sprint() は改行せずに出力。newline() は改行のみ。*/
for i in m do(
  for j in i do sprint(j),
  newline()
)$
1 0 0 0 
0 23 0 0 
0 0 456 0 
0 0 0 7890 
In [63]:
/* 書式指定で表示桁数を6桁とり,右寄せで各成分を表示 */
for i in m do(
  for j in i do printf(true, "~6d", j),
  newline()
)$
     1     0     0     0
     0    23     0     0
     0     0   456     0
     0     0     0  7890

参考:printf(true, )true 以外の使い方

書式指定で出力する際,printf(true, ) としているが,true 以外にも例えば false とかもあるのか,について。

すでに,以下のように true のところに書き込み用のファイルの設定を書く例は示している。

s: openw("myoutput2.txt")$
for i:1 thru 10 do(
  printf(s, "~,3f   ~,15f~%", 0.1*i, sin(0.1*i))
)$
close(s)$

 

以下は,printf(false, ) の例。false と書くと printf(false, ) は出力を含む文字列を返すので,文字列を別の変数(たとえば tmp)に代入したいときに使ったらどうでしょうか。

参考:0詰め連番ファイル名の作成

以下は 0 詰め連番ファイル名の作成例。

In [64]:
for i: 0 thru 3 do(
  tmp: printf(false, "~4d", 1000+i),
  filename: concat("fig", substring(tmp, 2), ".png"),
  print(filename)
)$
fig000.png
fig001.png
fig002.png
fig003.png