Python – 図形を含むアニメーション

ArtistAnimationの場合

ArtistAnimationにグラフを追加するには、たとえばplotメソッドの戻り値をそのままリストに追加していけばよい。

ところが、matplotlib.patchesの図形の場合に同じようにすると、アニメーションにならず全フレームの図形が重ね書きされてしまう。

これを解決するには、appendの際に[]で囲んでリスト化する必要がある。

以下は円の図形のアニメーションのコード例。

実行結果は以下の通り。

グラフと図形の重ね合わせ

グラフと図形を重ね合わせるときも、patchesの図形を明示的にリストとしてappendする必要がある。

以下は、plotによる線とpatches.Circleによる円を重ねて描く例。

実行結果は以下の通り。

FuncAnimationの場合

FuncAnimationの場合は描画関数中でグラフを描くだけで、ArtistAnimationのようなコレクションを用いない。

この場合はArtistAnimationより簡明で、グラフはAxesオブジェクトなどに描画、図形はadd_artistメソッドで追加するだけ。

以下はArtistAnimationと同じく、plotによる線とpatches.Circleによる円を重ね牡蠣する例。

 

Python – FuncAnimationによる動画表示

概要

Pythonのmatplotlibにはanimationパッケージがあり、ArtistAnimationFuncAnimationの2つのクラスが準備されている。

  • ArtistAnimationは、予め動画に表示する各フレームを生成しておいて、これらを動画として表示する
  • FuncAnimationは、全結果を保存せず、フレームごとに描画を実行する関数を実行して、動画して表示する

ここでは、FuncAnimationによる動画の表示方法を整理する(ArtistAnimationについてはこちら)。

1つのグラフの表示

流れ

  1. animationパッケージ又はArtistAnimationクラスをインポート
  2. グラフを描画する関数を定義
  3. FuncAnimationクラスのコンストラクターに描画関数を渡してインスタンスを生成
  4. 表示

処理ステップ

インポート

パッケージをインポートする

または

フレームリストの準備

動画の各フレームを保存するリストを準備する。animationパッケージでは、個々のグラフフレームをartistと表現している。

FigureとAxesオブジェクトを準備

フレームを描画するオブジェクトを準備しておく

1フレームを描画する関数を定義

4周期分の正弦関数を描画する関数を定義している。

  • ここではループごとにxを計算しているが、今回は全フレーム共通なので、ループの外で準備してもよい
  • plot命令のcolor指定をしないと、描画するごとに違うグラフとして色を変えて描画されてしまう

アニメーション設定して表示させる

FuncAnimationクラスのコンストラクターに必要な引数を与えてインスタンスを生成し、表示させる。intervalはフレーム間の待機時間で、ミリ秒で与える。

GIF画像として保存する

結果をGIF画像として保存するには、plt.show()を実行せず、以下のようにimagemagicでファイルとして書き出す。

frames=20でフレーム数を設定しているが、これがないとGIF保存のときにファイル容量が肥大化してしまう。それでも容量は800KB程度になり、ArtistAnimationのときの150KB程度よりも大きい。

実行結果

以下のような動画が表示される。

コード

全体の実行コードは以下のとおり。

複数のグラフの表示

流れ

全体の流れは単一のグラフの場合と同じ。リスト保存時に複数グラフを加算して重ね合わせるところだけが異なる。

  1. animationパッケージ又はArtistAnimationクラスをインポート
  2. グラフを描画する関数を定義し、複数のグラフをプロット
  3. FuncAnimationクラスのコンストラクターに描画関数を渡してインスタンスを生成
  4. 表示

処理ステップ

以下の描画関数が単一グラフの場合と異なる。関数の中で、異なる2つのグラフを描画している

実行結果

以下のように、2つのグラフが同時に表示される。

コード

全体の実行コードは以下のとおり。

描画関数に引数を渡す

描画関数に独自の引数を渡すことができる。

たとえば最初の「1つのグラフの表示」で、frame_countと表示する最大時刻max_xを引数とする場合、まず描画関数でこれらを引数として宣言し、関数内でこれを使った処理を書く。

FuncAnimationインスタンス生成時に、fargsで独自宣言した引数の値をタプルで与える。

 

Python – ArtistAnimationによる動画表示

概要

Pythonのmatplotlibにはanimationパッケージがあり、ArtistAnimationFuncAnimationの2つのクラスが準備されている。

  • ArtistAnimationは、予め動画に表示する各フレームを生成しておいて、これらを動画として表示する
  • FuncAnimationは、全結果を保存せず、フレームごとに描画を実行する関数を実行して、動画して表示する

ここでは、ArtistAnimationによる動画の表示方法を整理する(FuncAnimationについてはこちら)。

1つのグラフの表示

流れ

  1. animationパッケージ又はArtistAnimationクラスをインポート
  2. フレームの数だけグラフを描画してリストに保存
  3. フレームリストを渡してArtistAnimationインスタンスを生成
  4. 表示

処理ステップ

インポート

パッケージをインポートする

または

フレームリストの準備

動画の各フレームを保存するリストを準備する。animationパッケージでは、個々のグラフフレームをartistと表現している。

FigureとAxesオブジェクトを準備

フレームを描画するオブジェクトを準備しておく

フレーム数を指定して各フレームを保存する

以下ではフレーム数を20として、20回分のグラフを少しずつずらして描画して保存している。

ここではグラフを正弦波として、1周期分を20分割して表示させている。

  • ここではループごとにxを計算しているが、今回は全フレーム共通なので、ループの外で準備してもよい
  • plot命令のcolor指定をしないと、描画するごとに違うグラフとして色を変えて描画されてしまう
  • 描画結果はオブジェクトとして、先に準備したartistコレクションに追加している

アニメーション設定して表示させる

ArtistAnimationクラスのコンストラクターに必要な引数を与えてインスタンスを生成し、表示させる。intervalはフレーム間の待機時間で、ミリ秒で与える。

GIF画像として保存する

結果をGIF画像として保存するには、plt.show()を実行せず、以下のようにimagemagicでファイルとして書き出す。

実行結果

以下のような動画が表示される。

コード

全体の実行コードは以下のとおり。

複数のグラフの表示

流れ

全体の流れは単一のグラフの場合と同じ。リスト保存時に複数グラフを加算して重ね合わせるところだけが異なる。

  1. animationパッケージ又はArtistAnimationクラスをインポート
  2. フレームの数だけグラフを描画して、重ね合わせてリストに保存
  3. フレームリストを渡してArtistAnimationインスタンスを生成
  4. 表示

処理ステップ

以下の部分だけが単一グラフの場合と異なる。2つのグラフを描画し、そのオブジェクトを加算して1フレームとした上でartistsに追加している

実行結果

以下のように、2つのグラフが同時に表示される。

コード

全体の実行コードは以下のとおり。

図形を含むアニメーション

matplotlib.patchesによる図形を含んでアニメーション表示するには、少し工夫がいる。その方法はこちら

 

Runge-Kutta法による1階常微分方程式の解

概要

1階常微分方程式について、4次のRunge-Kutta法(RK4)による数値解と解析解を比較する。Euler法による解についても重ねてみた。

  • RK4は、かなり粗い分割間隔でもよい結果になる
  • Euler法は、分割間隔を小さくとらないと精度が上がらない

Logistic関数

方程式と解析解

Logistic関数に関する微分方程式は以下で表される。

(1)    \begin{equation*} \frac{N(t)}{dt} = r \left( 1 - \dfrac{N(t)}{K} \right) N(t) \end{equation*}

この方程式は解析的に解けて、以下の解を得る。

(2)    \begin{equation*} N(t) = \dfrac{K}{1 + \left( \dfrac{K}{N_0} - 1 \right) e^{-rt}} \end{equation*}

数値解

式(1)をRK4、Euler法によって解き、解析解と重ねる(解き方は1階常微分方程式の数値解法を参照)。

r=10, K=1000として計算した結果は以下の通り。

  • RK4は上限に収束するまでの時間(t=1.0)の20等分くらい(dt=0.05)でもよく合っている
  • Euler法では凹な部分で計算値が過少となり、凸の部分で解析解に追いついているが、適合度はよろしくない。

dtを上記の10分の1(dt=0.05)として計算した結果が以下の通り。Euler法もかなり適合度合いがよくなったが、それでもまだ少し右方にずれている。

逆にdt=0.1と大きくしてみた。折れ線がはっきりわかるくらいの粗さだが、RK4はよくフィットしている。Euler法のずれは大きくなっている。

コード

上記の結果は、以下のコードを実行して得ている。

三角関数

方程式と解析解

簡単な正弦関数を考える。

(3)    \begin{equation*} y = \sin t \end{equation*}

この関数は、以下の微分方程式の解である。

(4)    \begin{equation*} y' = \cos t \quad ( t=0 \Rightarrow y=0 ) \end{equation*}

数値解

式(4)をEuler法、Runge-Kutta法によって解き、式(3)のグラフと重ねる。

以下は分割間隔を1/40周期で計算した結果。RK4の精度はいいが、Euler法では上に凸のときに過大となり、下に凸のときに過少となって初期値に戻っている。

分割幅を1/200周期にするとEuler法でも精度は上がってくるが、まだ少し誤差が残っている。

分割数を10とかなり粗くとっても、RK4の適合度合いはよい。

繰り返し数が増えた時に誤差が累積するかどうか確認してみた。

以下は10万周期分計算した最後の2周忌を取り出した結果で、RK4、Euler法とも傾向は変わらない。規則正しい周期関数の場合は、繰り返し数を増やしても誤差は拡散しないと思われる。

コード

以下はこれまでの計算をしたPythonのコード。

計算区間を長くとって一部だけ表示させるため、Logistic関数のときとは異なる処理をしている。

  • ループの各段階で計算結果をリストに保存せず、各ステップで計算された次のステップの結果を元の変数に入れ替える
  • 予め指定した計算区間の間の結果だけ、表示用のリストに保存する

 

2階常微分方程式の数値解法

概要

2階常微分方程式の数値解法、Euler法とRunge-Kutta法をPythonのモジュールとしてまとめた。

1階常微分方程式の解法についてはこちら

基本形

以下のような方程式を考える。

(1)    \begin{equation*} \dfrac{d^2 y}{dx^2} = f\left ( x, y, \dfrac{dy}{dx}, ... \right ) \end{equation*}

(2)    \begin{equation*} y'' = f(x, y, y', ... ) \end{equation*}

y‘(x)をv(x)と置いて以下のように変形する。

(3)    \begin{align*} \frac{dy}{dx} &=y' = v \\ \frac{dv}{dx} &= v' = f(x, y, y', ...) \end{align*}

Euler法

Euler法では、iステップ目のΔv→Δyと計算し、それらを使ってi+1ステップ目のyvを計算する。

(4)    \begin{align*} \Delta v_i &= \Delata x \cdot f(x_i, y_i, y'_i, ...) \\ \Delta y_i &= \Delta x \cdot v_i \\ y_{i+1} &= y_i + \Delta y_i \\ v_{i+1} &= v_i + \Delta v_i \end{align*}

Runge-Kutta法

Runge-Kutta法も同様に、iステップ目のΔv1→Δy1を計算し、それらを使ってΔvi、Δyi (i = 1, 2, 3, 4)を計算していく。

(5)    \begin{align*} \Delta v_{1, i} &= \Delta x \cdot f(x_i, y_i, v_i, ...) \\ \Delta y_{1, i} &= \Delta x \cdot v_i \\ \Delta v_{2, i} &= \Delta x \cdot f \left ( x_i + \dfrac{\Delta x}{2}, y_i + \dfrac{\Delta y_{1, i}}{2}, v_i + \dfrac{\Delta v_{1, i}}{2}, ... \right ) \\ \Delta y_{2, i} &= \Delta x \cdot \left (v_i + \dfrac{\Delta v_{1, i}}{2} \right ) \\ \Delta v_{3, i} &= \Delta x \cdot f \left ( x_i + \dfrac{\Delta x}{2}, y_i + \dfrac{\Delta y_{2, i}}{2}, v_i + \dfrac{\Delta v_{2, i}}{2}, ... \right ) \\ \Delta y_{3, i} &= \Delta x \cdot \left (v_i + \dfrac{\Delta v_{2, i}}{2} \right ) \\ \Delta v_{4, i} &= \Delta x \cdot (v_i + \Delta v_{3, i}) \\ \Delta y_{4, i} &= \Delta x \cdot f(x_i + \Delta x, y_i + \Delta y_{3, i}, v_i + \Delta v_{3, i}, ...) \\ x_{i+1} &= x_i + \Delta x \\ y_{i+1} &= y_i + \frac{\Delta y_{1, i} + 2 \Delta y_{2, i} + 2 \Delta y_{3, i} + \Delta y_{4, i}}{6} \\ v_{i+1} &= v_i + \frac{\Delta v_{1, i} + 2 \Delta v_{2, i} + 2 \Delta v_{3, i} + \Delta v_{4, i}}{6} \end{align*}

数値解法モジュール

コード

Euler法とRunge-Kutta法で2階常微分方程式を解くモジュールを、Pythonで以下の様に書いた。

関数euler2rk4_2の使い方はいずれも同じで、以下の通り。

  • x, y, v:現在の計算ステップでの独立変数x、xにおけるyの値、xにおけるyの1次微分値
  • dx:計算の分割幅
  • func:微分方程式の右辺の関数
  • params:関数funcに与えるパラメーター

パラメーターは、呼び出し側の関数定義で設定したパラメーター名をキーとした辞書で与える。その前提で、モジュール内で**paramsとしてアンパックしている。

戻り値は実数型の数値。

使い方

モジュールの使い方の概要は以下のとおり。

  1. 右辺の関数を定義する
  2. 関数に渡すパラメーターを設定する
  3. 計算開始点と終了点のxの値、分割幅dx, yvの初期値を設定する
  4. x, y, vの計算用リストを確保する
  5. 計算開始点のyvのリストに初期値をセットする
  6. 計算実行

単純な放物線の微分方程式を例にしてモジュールの使い方を示す。まず、解の方程式として以下を考える。

(6)    \begin{equation*} y = a x^2 \quad (-1 \le x \le 1) \end{equation*}

この式を2階微分した、以下の常微分方程式を解いていく。

(7)    \begin{equation*} y'' = 2a \quad \left( x = -1 \Rightarrow \left\{ \begin{array}{l} y = a\\ y' =-2a \end{array} \right) \end{equation*}

まず、必要なパッケージをインポート。diffeqは上で示したモジュールを収めている。

次に、微分方程式右辺の関数を定義する(以下のderivative)。この関数はパラメーターとして係数aを1つ持っている。また、パラメーターの値を辞書で定義する。

数値計算に計算開始点x_startと終了点x_end、分割幅dxyvの計算初期値を設定。

次に、予めxの計算点のリストを用意して、これに対応するy, vの値を計算・保存していく。計算点数は植木算で分割数+1とし、ループ計算に用いるリストは計算終了点を除いている。結果を保存しないなら、リストではなく逐次各ステップの計算値を次のステップの入力値にする。

また、計算開始点におけるy, vの初期値をセットしている。

計算自体はシンプルで、定義域リストの値を1つずつ進めながら、現在の値や関数への参照、パラメーターを与えてモジュールを呼び出す。

実行例

元の解析解と併せて表示した実行結果は以下の通りで、1回微分方程式の場合と同じく、Euler法の解は計算が進むごとに乖離が大きくなっている。Runge-Kutta法の解はかなり正確に解析解と一致している。

以下は、解析解も含めてグラフ表示するための、全体の実行コード。

 

1階常微分方程式の数値解法

概要

1階常微分方程式の数値解法、Euler法とRunge-Kutta法をPythonのモジュールとしてまとめた。

2階常微分方程式の解法についてはこちら

基本形

以下のような方程式を考える。

(1)    \begin{equation*} \dfrac{dy}{dx} = y' = f(x, y, ...) \end{equation*}

Euler法

Euler法はシンプルな方法で、ある計算ステップiの時の関数上の点(xi, yi)とその点における微分係数yiを使って、計算幅dxだけ先にある点xi+1 = xi + dxのに対応するyiの値を近似的に計算する。

(2)    \begin{align*} \Delta y_i &= \Delta x \cdot f(x_i, y_i, ...) \\ x_{i+1} &= x_i + \Delta x \\ y_{i+1} &= y_i + \Delta y_i \end{align*}

概念図からもわかるように、たとえば下に凸の関数の場合、微分係数で線形的に予測した値は常に真値より小さくなるため、誤差が拡大していくことが予想される。

Runge-Kutta法

Runge-Kutta法には1次、2次など異なる次数の解法があるが、4次の方法がよく紹介されている。

(3)    \begin{align*} \Delta y_{1, i} &= \Delta x \cdot f(x_i, y_i, ...) \\ \Delta y_{2, i} &= \Delta x \cdot f \left ( x_i + \dfrac{\Delta x}{2}, y_i + \dfrac{\Delta y_{1, i}}{2}, ... \right ) \\ \Delta y_{3, i} &= \Delta x \cdot f \left ( x_i + \dfrac{\Delta x}{2}, y_i + \dfrac{\Delta y_{2, i}}{2}, ... \right ) \\ \Delta y_{4, i} &= \Delta x \cdot f(x_i + \Delta x, y_i + \Delta y_{3, i}, ...) \\ x_{i+1} &= x_i + \Delta x \\ y_{i+1} &= y_i + \frac{\Delta y_{1, i} + 2 \Delta y_{2, i} + 2 \Delta y_{3, i} + \Delta y_{4, i}}{6} \end{align*}

これを説明するのに、xy平面上での関数を描き、その微分係数を複数描いているものが多い。

本来は、右辺のz = f(…)上のある曲線があって、それをxy平面に投影した曲線のある点における微分係数と、その点におけるzの値が等しくなるような曲線を求めるという意味となる。実際にzの値と投影曲線の微分係数が等しいというのを直感的に示すのはやっかいだなと思っていた。

いろいろ探してみると、どうやらこの式形は元の関数をTaylor展開して、次数を上げて精度を高めるというものらしい。文献を見てみると、その導出過程でかなり長々とした式が出ていた。時間がある時に試してみたい。

数値解法モジュール

コード

Euler法とRunge-Kutta法で1階常微分方程式を解くモジュールを、Pythonで以下の様に書いた。

関数euler1rk4_1の使い方はいずれも同じで、以下の通り。

  • x, y:現在の計算ステップでの独立変数xと関数値yの値
  • dx:計算の分割幅
  • func:微分方程式の右辺の関数
  • params:関数funcに与えるパラメーター

パラメーターは、呼び出し側の関数定義で設定したパラメーター名をキーとした辞書で与える。その前提で、モジュール内で**paramsとしてアンパックしている。

戻り値は実数型の数値。

使い方

モジュールの使い方の概要は以下のとおり。

  1. 右辺の関数を定義する
  2. 関数に渡すパラメーターを設定する
  3. 計算開始点と終了点のxの値、分割幅dx, yの初期値を設定する
  4. x, yの計算用リストを確保する
  5. 計算開始点のyのリストに初期値をセットする
  6. 計算実行

単純な放物線の微分方程式を例にしてモジュールの使い方を示す。まず、解の方程式として以下を考える。

(4)    \begin{equation*} y = a x^2 \quad (-1 \le x \le 1) \end{equation*}

この式を1階微分した、以下の常微分方程式を解いていく。

(5)    \begin{equation*} y'= 2ax \quad (x = -1 \Rightarrow y = 1) \end{equation*}

まず、必要なパッケージをインポート。diffeqは上で示したモジュールを収めている。

次に、微分方程式右辺の関数を定義する(以下のderivative)。この関数はパラメーターとして係数aを1つ持っている。また、パラメーターの値を辞書で定義する。

数値計算に計算開始点x_startと終了点x_end、分割幅dxyの計算初期値を設定。

次に、予めxの計算点のリストを用意して、これに対応するyの値を計算・保存していく。計算点数は植木算で分割数+1とし、ループ計算に用いるリストは計算終了点を除いている。結果を保存しないなら、リストではなく逐次各ステップの計算値を次のステップの入力値にする。

また、計算開始点におけるyの初期値をセットしている。

計算自体はシンプルで、定義域リストの値を1つずつ進めながら、現在の値や関数への参照、パラメーターを与えてモジュールを呼び出す。

実行例

元の解析解と併せて表示した実行結果は以下の通りで、予想通りEuler法の解は計算が進むごとに乖離が大きくなっている。Runge-Kutta法の解はかなり正確に解析解と一致している。

以下は、解析解も含めてグラフ表示するための、全体の実行コード。

 

放物線の焦点

概要

放物線が、その対称軸に平行な線に対して焦点を持つことを確認する。イメージ図は以下の通り。

証明

下図を考える。

放物線Cを以下の式で定義する。

(1)    \begin{equation*} C: \: y_0(x) = a x^2 \end{equation*}

任意の点(X, Y)における接線l1の式は以下の様になる。

(2)    \begin{align*} l_1: \: y_1(x) &= y_1' (X) \cdot (x - X) + Y &= 2aX (x - X) + X^2 \end{align*}

ここで、y1‘は、図中の角θを用いて以下のように表せる。

(3)    \begin{equation*} y_1'(X) = \tan \theta \quad \left( -\frac{\pi}{2} \le \theta < \frac{\pi}{2} \right) \end{equation*}

また、この点を通ってl1に垂直な直線l2の式は、この点におけるl2の微分係数をy2‘として以下のように表せる。

(4)    \begin{equation*} l_2: \: y_2(x) = y_2' (X) \cdot (x - X) + Y \end{equation*}

ここでy2‘は、図中の角θを用いて以下のように導かれる。

(5)    \begin{align*} y_2'(X) &= - \tan \left( \dfrac{\pi}{2} - 2 \theta \right) \\ &= - \frac{\cos 2 \theta}{\sin 2 \theta} \\ &= - \frac{\cos^2 \theta - \sin^2 \theta}{2 \sin \theta \cos \theta} \\ &= - \frac{-1 + \tan^2 \theta}{2 \tan \theta} \end{align*}

この式でθπ/2よりも大きい場合でも、式形は変わらない。

ここで式(3)を考慮して、l2の式は以下のように変形される。

(6)    \begin{equation*} y_2(x) = \frac{-1 + 4 a^2 X^2}{4aX} (x - X) + a X^2 \end{equation*}

l2y軸と交わる点のy座標値を求める。

(7)    \begin{align*} y_2(0) &= \frac{-1 + 4 a^2 X^2}{4aX} (-X) + a X^2 \\ &= \frac{X - 4 a^2 X^3 + 4a^2 X^3}{4aX} \\ &= \frac{1}{4a} \end{align*}

この値はXに寄らず一定なことから、y軸に平行な線が放物線で反射された線は、y軸上の1点を通る。

なお、入射線と反射線が直角になるときの入射線のx座標値は以下の通り。

(8)    \begin{align*} a x^2 = \frac{1}{4a} \quad \Leftrightarrow \quad x = \plusminus \frac{1}{2a} \end{align*}

斜め入射について

たとえば先の図で、入射角が右側に傾いた場合を考える。

y軸の右側(xが正の側)からの入射線の放物線に対する角度は、y軸に平行な場合に対してより浅くなることから、反射方向はより下側に傾く。したがって、反射線とy軸の交点は下側に移動する。

一方y軸の左側(xが負の側)からの入射線の放物線に対する角度は、y軸に平行な場合に対してより深くなることから、反射方向はより上側に傾く。したがって、反射線とy軸の交点は上側に移動する。

少なくともy軸の両側で反射線のy軸との交点が一致しないことから、焦点は存在しない。より正確には、入射線のy軸に対する傾きをαなどと置いて上記のような計算をするとよい。

 

PythonCGI – 基本

概要

PythonのコードでCGI(Common Gateway Interface)によってHTMLをする方法の基本。

基本の枠組み

コード例

例えば以下の内容をindex.pyとして準備。

  1. SheBangでPythonの場所を指定
  2. Content-Typeを出力して1行空ける
  3. 出力内容を記述

HTTPのヘッダーで最低限Content-Typeを出力し、ボディーとの間に空行が1行必要。これがないと、500エラーとなり、ログには以下の様に記録される。

コードのモード確認

ファイルがrootの実行権限を持っていること。実行権限がない場合はchmod 755chmod +xで設定する。

コードの配置

cgi実行用のディレクトリーにファイルを配置。Apacheの場合例えば/var/www/cgi-binなど。

ブラウザ表示

URLで[ホスト]/cgi-bin/index.pyを指定すると、ブラウザーにPython CGI Testと表示される。

HTML出力例

ASCII出力

基本の枠組みの出力に、以下の様にタグを加えると要素として表示される。

UTF-8出力

上のコードの一部を日本語にしてみる。

ところがこれをブラウザーで確認すると、h1要素は表示されるがp要素が全く表示されない。ソースを確認してもh1要素の行が表示されるだけ(このコードをコンソールで実行すると、h1要素の行もp要素の行も表示される)。

これを解決するには、ソースコードでio.TextIOWrapperを実行するとよいらしい。

上記の2行を追加すると、無事日本語まで表示された。

HTML出力

コンテンツを以下の様にHTMLの構造にして出力する。title要素も機能して、タブの表示が変更された。

ループの利用例

以下のコードでは、見栄えは良くないがforループでli要素を生成している。

出力に以下が加わる。

  1. 1番目のli要素
  2. 2番目のli要素
  3. 3番目のli要素
  4. 4番目のli要素
  5. 5番目のli要素

Apacheの設定

今回は/etc/httpd/conf/httpd.confのデフォルトのままでcgi-scriptの設定を加えていないが、そのままで適切に実行された。

 

2次元リストの結合

概要

2次元リスト同士の結合について整理。

  • 縦方向の結合
    • appendはループが必要
    • extendは破壊的に結合
    • +演算子は非破壊的に結合
  • 横方向の結合
    • extendでループが必要
    • +演算子はループの方法に注意

縦方向の結合

appendは要注意

1次元のリストの場合と同じく、appendは引数全体を要素として追加してしまう。

追加されるリストの各行を取り出して追加すれば可能。ただし破壊的。

extendはok(破壊的)

extendは引数のリストの内容をそのまま追加してくれる。ただし破壊的。

+演算子もok(非破壊的)

+演算子もextendと同じ挙動だが非破壊的。

横方向の結合

extendで一手間必要

extendで各要素に追加する場合、各要素をループで取出して追加する必要がある。

カウンターではなく要素を直接取り出してもok。

+演算子は要注意

+演算子で結合する場合は、結果のリストを準備しておく必要がある。このとき、結合するリストと同じ要素数(行数)の空のリストを要素に持つリストを準備しておく必要がある。

また、ループはカウンターを使って添字でリスト要素を指定しなければならない。

要素を直接取り出して演算した場合、変数が新たに作成されるがその結果は元のリストには反映されないので注意。

 

リストの追加・結合

概要

リストに対する要素の追加、リストの結合について整理。

  • リストへの1つの要素の追加
    • appendが簡明、ただし破壊的
    • extendも使えるがリストとして追加、破壊的
    • +演算子もリストとして追加、非破壊的
  • リスト同士の結合
    • appendは使えない
    • extendは破壊的にリストを追加
    • +演算子は非破壊的にリストを追加

リストへの要素の追加

appendは破壊的

appendで1つの要素をリストに追加できる。appendは破壊的で元のリストを改変する。戻り値はNone

appendは1つの引数しか持てない(append(4, 5)とはできない)。

extendはリストを追加(破壊的)

extendでも要素を追加できるが、要素そのものではなく、追加したい要素を含むリストとして追加する。extendは破壊的で元のリストを改変する。戻り値はNone

+演算子もリストの演算(非破壊的)

+演算子もextendと同じくリストを追加する。extendと違って非破壊的で、元のリストは改変されず、結果は戻り値で返される。

リストの結合

appendは使えない

appendは引数をリストの要素として追加するので、リストを引数に与えるとそのリストが要素として追加されてしまう。

extendはOk(破壊的)

extendは引数に与えたリストで元のリストを拡張する。ただし破壊的で元のリストが改変される。

+演算子もOk(非破壊的)

+演算子もextendと同じくリストを拡張するが非破壊的。拡張と言うよりも、くっつける/追加するというイメージ。