Author Archives: セニョール

C言語プログラマのためのPython入門(第1回)

この講座は、普段、C言語でソフトウェアを開発されている「組み込みソフトウェア技術者」の皆様に向けて、pythonの活用例をご紹介する講座です。その第1回では、pythonの言語仕様について、C言語と比較しながら、ポイントをご紹介します。

関数

pythonでは、C言語と同じく、関数を記述し、実行できます。ただしC言語では、関数が必須であるのに対して、pythonでは関数を書いても良いし、書かなくても良い…という違いがあります。関数を作成せずにpythonのソースコードを書いた場合、記述順に従い、(制御文で分岐させない限り)基本、上から下に順番に実行されます。

(補足)
ソースファイルの上から順番に処理を書く場合でも、その途中に、必要に応じてに関数を書く事も可能です。この場合は、関数「以外」の処理が上から順に実行され、関数の中身は「関数呼び出し」が記述された場合のみ、その呼び出し箇所から呼ばれ、実行されます。

pythonの関数の記述例を以下に示します。

def addAndCheck_over100(var1, var2):

result = var1 + var2

isOver100 = False

if (result > 100):

isOver100 = True

return (result, isOver100)

  • 関数先頭の書式は「def 関数名(引数, …):」です。末尾のコロン’:’は必須です。引数が無い場合は括弧()内は空にします。
  • 引数や戻り値の「型宣言」は不要です。「型」については、後の節で解説します。
  • pythonでは、変数を使用する際、事前に宣言する必要は無く、いきなり「初期化」から記述できます。
  • pythonの関数の大きな特徴は、複数の値を「戻り値」として返せる点です。

上記した関数の呼び出し元の記述例は、以下です。複数の戻り値のコピー先をカンマ(,)で区切って、左から順に並べます。

value, isOver = Add_andCheck_over100(48, 32)

型宣言

C言語では、変数を宣言する際、必ずその「型」を宣言しましたが、pythonでは型宣言は不要です。
pythonにおける変数の型は、その変数に値を「代入」した時に決定されます。また既に値を代入した変数に対して、別の型の値を代入すると、その時点で変数の型が変わります。

var1 = 10

print(var1)

print(type(var1))

var1 = "ABC"

print(var1)

print(type(var1))

上記したpythonソースコード中の「type()」は、引数で指定した変数の「型名」を文字列で返す関数です。上記のソースを実行した場合、画面には以下が表示されます。

10

<class 'int'>

ABC

<class 'str'>

同じ変数var1に対して、異なる型である「文字列型」の値を代入したことで、変数の型がint型から文字列(str)型に変化しています。ただ上記のように、同じ変数に異なる型の値を代入するようなソースコードを積極的に書く必要は無く、pythonの言語仕様として、そういう特徴がある..という事だけ覚えて頂ければと思います。

ブロック

C言語では、ブロックを{}で表現しましたが、pythonでは「インデント」で表現します。すなわち、同じ階層の「インデント」が同じブロックになります。

if (a > 10):

if (b > 20):

print("OK!")

インデントは、タブまたは半角スペースで記述できますが、同じ階層のインデントは、同じ文字で記述する必要があります。たとえばタブを使った場合、その階層のインデントは、全てタブで段下げする必要がありますし、半角スペースを使った場合は、全て半角スペースで段下げする必要があります。

タブとスペースは、無理に混在させる必要は無いと思いますので(C言語でも同様)、普段お使いの文字で段下げされれば良いと思います。

文字

この後にご紹介するpythonの活用法では、テキストファイル/CSVファイル/EXCELファイル等のファイルを読み込み、処理する活用法をご紹介する予定です。それに向けた予備知識として、本節ではpythonにおける「文字」の扱いについて説明します。

C言語では「文字」が連続したものが「文字列」ですが、pythonでは1文字だけを表現する「文字型(char型)」は存在しません。pythonにおいて「文字」を扱う場合は「1文字だけの文字列」として扱う必要があります。この点は、C言語と大きく異なる点ですが、慣れてくると、特に不都合を感じる事はありません。

たとえば、文字列を先頭から1文字ずつ参照し、文字’A’ と同じか判定したい場合は、以下のように記述します。

str1 = "ABC"          # C言語の記述と似ている。ただし文字列終端のnull文字('\0')は存在しない。

length = len(str1)       # 文字列のバイト長は関数len で取得可能。結果は3。

for i in range(length):     # iの値0から2でループさせたい場合 ※for文と range()は後節で解説

if (str1[i] == "A"):    # []は「スライス」と呼ばれる。※後節で解説

print("Matched! at index:%d" % i)


(補足)

  • 文字列の前後を囲む文字は ” でも ‘ でも、どちらでも使用できます。
  • 文字列から「1文字だけの文字列」を取り出す場合は[]を使用します。これは「スライス」と呼ばれるものです。
  • スライスについては、後の節で解説します。

C言語では、1文字を判定(もしくは変換)する関数として、isdigit()、toupper() 等の関数が用意されていますが、pythonでは、同様の関数が「文字列全体」に対して用意されています。たとえば isdigit()は「文字列全体が全て10進数ならTrue、否ならFalseを返す」関数として用意されています。

toupper()は、pythonでは関数名がupper()に変わりますが、その機能は「アルファベットの小文字は大文字に変換し(それ以外の文字は変換せずに)、処理後の文字列を返す」関数として用意されています。

str1 = "abc1"

length = len(str1)

for i in range(length):

if (str1[i].isdigit() == True):

print("Digit! at index:%d" % i)

else:

convStr = str1[i].upper()

if (convStr == "A")

print("Matched! at index:%d" % i)

「クラス」と「メンバ関数」について

上記のソースにおいて、関数isdigit および 関数upperは、関数lenとは異なり、文字列型の変数str1の「変数名の後にピリオド’.’を付けて」その後に関数名を指定して呼び出しています。その理由は、関数isdigit および 関数upperは、文字列型の「クラス」に属する「メンバ関数」として実装されているためです。

C++やJava等、クラスが使えるプログラミング言語の経験のある方々にとっては、馴染みのあるこの「メンバ関数」ですが、この種の(オブジェクト指向の)プログラミング言語のご経験の無いC言語プログラマの方々にとっては、馴染みの薄い事柄だと思います。

ここで少しだけ「クラス」および「メンバ関数」について補足します。

  • 「クラス」は、C言語における「構造体」に似ていますが、メンバとして「関数」が追加できます。
  • メンバとして追加した関数を「メンバ関数」と呼びます。
  • 「メンバ関数」では、(一般的には)他の「メンバ変数」の参照/変更に関わる処理を行います。

python において、どのような関数が関数lenのよう(グローバル関数のよう)に提供され、またどのような関数が「メンバ関数」として提供されているかは「こんな関数を使いたい」という「目的」が生じた時点で、ネット検索で確認してください。

配列(pythonでは「リスト」と呼びます)

pythonにおいても(C言語と同様に)配列が使用できます。pythonでは配列のことを「リスト」と呼びます。以下、リストの使用例を示します。

arr1 = [0,1,2]      # リストを初期化

print(arr1)        # [0, 1, 2] が表示される

arr1[1] = 100       # 1番目の要素を変更(インデックスはゼロ始まり)

print(arr1)        # [0, 100, 2] が表示される

arr2 = arr1        # リストを別の変数にコピー ⇒中身では無く「参照」がコピーされ、同じ実体を指す

arr2[1] = 11       # コピー先の1番目の値を書き換えた後、以下でコピー元のリストを表示

print(arr1)        # [0, 11, 2] が表示される(「参照」のコピー故、コピー元のリストも変化)

arr1.clear()       # メンバ関数clearで全データを消去

print(arr1)        # [] が表示される

arr1.append(101)     # メンバ関数appendでデータを追加

print(arr1)        # [101] が表示される

def func(arr, dt):    # 引数arrでリストの「参照」を受け、引数dtで設定する値を受ける

arr[0] = dt      # 0番目の要素を書き換え

func(arr1, 200)      # 上記関数を呼び出す

print(arr1)        # [200] が表示される

あるリストを別のリストに「代入」すると、中身が「コピー」されるのでは無く「参照」がコピーされます。すなわち「代入」した後は、コピー先、コピー元 共に、同じ「実体」を指すようになります。この特徴は「代入」だけで無く、「関数の引数」にリストを渡したときも同じです。

なお、この「参照」では無く、リストの中身をコピーしたいときもあると思います。この場合は、以下のように記述することで、「参照」では無く「中身がコピー」されます。

arr2 = []        #空のリストで初期化

arr2 += arr1      #そこに既存のリストarr1 の中身を追加(まとめて arr2 = [] + arr1 と書いても可)

ポインタ

pythonでは「ポインタ」は使えません。またJavaのような「参照」も、前節でご紹介した「リスト」等、一部の型でしか使えません。ですので、関数の引数に「整数型」や「文字列型」の変数アドレスを渡して、関数内で中身を「書き換える」こともできません。

この対策として、筆者は関数から「複数の戻り値」を返して、元の変数に「代入」する事で対処しています。

def proc(a, b, c):

b = a + b

c = b + c

a = c + a

return (a, b, c)


var1 = 1

var2 = 2

var3 = 3

var1, var2, var3 = proc(var1, var2, var3)

文字列のコピー

C言語では、strcpyやmemcpyを使って、文字列の中身をコピーしましたが、pythonでは「代入」によって文字列を「丸ごと」コピーできます。

str1 = "ABC"

str2 = str1

str2 = "012"

print(str1)        # ABC が表示される

print(str2)        # 012 が表示される

文字列は、一見するとC言語の配列のように見えますが、実は、文字列は「文字列(str)型」であり、リストとは異なり、「文字列中の一部の文字だけ書き換える」事はできません。たとえば以下は禁止されます。

str1 = "ABC"

str1[1] = 'b'

このような場合は、後節で説明する「スライス」を使って新たな文字列を作成し、その新たに作成した文字列「全体」をコピーし直します。

newStr = str1[0:1] + "b" + str1[2:]

str1 = newStr

スライス

先のpythonのソースコード中の[]は「スライス」と呼ばれ、文字列や配列(pythonでは配列のことを「リスト」といいます)の「一部を取り出す」ために使用します。書き方は以下です。

配列[開始位置:終了位置]

  • 開始位置は、先頭を「ゼロ番目」とするインデックス番号で指定します。
  • 終了位置も先頭を「ゼロ番目」とするインデックス番号で指定しますが、注意点として「終了位置より1つ大きい位置を指定する」必要があります。
  • たとえば文字列”ABC”が格納された変数str1の先頭2文字分を別物の文字列として取り出したい場合は str1[0:2] と指定します。

  • 開始位置、終了位置、共に省略できます。
  • 開始位置を省略した場合は、ゼロが指定されたと見なされます。終了位置を省略した場合は最後、すなわち最終位置より1つ大きい位置が指定されたと見なされます。

  • コロン’:’と終了位置を省略し、開始位置だけ指定することも可能です。
  • この場合は開始位置から1つ分の要素だけ指定したと見なされます。

(補足)

以前の節でご紹介したとおり、文字列の場合は「別物の文字列」が生成され、またリストの場合は「参照」が取り出されます。リストの場合は、スライスで取り出した一部の中身を「書き換える」ことが可能です。

list1 = [0,1,2,3]

list1[1] = 101          #書き換え可能

list1[2:] = [102,103]      #書き換え可能

for文

先のpythonのソースコード中の「for i in range(length):」は、C言語における「for (int i = 0; i < length; i++) {」に相当する記述でした。 pythonにおけるfor文では「for 変数名 in」の後に「配列」「リスト」等の「データの集合」を指定します。 たとえば先のpythonのソースコードは、文字列の先頭から1文字ずつ、別物の(1文字から成る)文字列として、スライスを使って取り出していましたが、これと同じ処理は、以下のように記述できます。

for chStr in str1:            # 文字列str1の先頭から1つずつ要素を取り出す

if (chStr == “A”):

ただこの方法では「何番目か」を」示すインデックス番号が取得できないです。このような用途に対して、pythonでは、以下の記述も使えます。

for i, chStr in enumerate(str1):    # 文字列str1の先頭から1つずつ要素を取り出す

if (chStr == "A"):

print("Matched! at index:%d" % i)

range()

本節では先のpythonのソースコードのfor文で使用した「range()」について説明します。rangeは「range型(クラス)」で、range()は「range型のオブジェクトの実体を生成して返す」関数です。
range型は、引数で指定した値に応じて、連続した値を要素として持つデータ集合です。たとえば「range(3)」として指定すると、この関数は「0,1,2」の連続する3つの値を持つオブジェクトを返します。

rangeは、for文以外ではあまり使う事が無いと思いますので、rangeについての説明は、この程度に留めます。この説明をご覧になられて混乱された方は、慣れるまで rangeは、上記したpythonソースの例のように「for文でしか使わない」と割り切って使って頂いて大丈夫と思います。

書式変換

C言語では、printf文において”%d” や “%x” 等の書式変換が使用できますが、pythonでも、基本、同様の書式変換が可能です。

書式付き文字列 % (引数1, 引数2, 以降も続く)

引数が1つの場合は、 書式付き文字列 % 引数1として記述し、%の後の括弧()は不要です。pythonでは、上記の書式変換は、print関数に限らず「文字列に対する操作」として使用できます。よって前の節に記述したpythonの画面表示のソースコードは、以下のように書き換える事も可能です。

resultStr = "Hit at index:%d" % i

print(resultStr)


(補足)関数printは、C言語のprintfに相当する画面表示関数です。

ソースファイルの実行方法

  • pythonのソースファイルの拡張子は.pyです。この拡張子でソースファイルを作成してください。
  • pythonのソースファイルは事前にコンパイルする必要は無く、いきなり、DOSプロンプト上で「python ソースファイル名.py」で実行できます。
  • pythonのソースファイル実行時、言語仕様 他に違反したコード記述を実行した場合、「例外」が発生し、実行が途中終了します。
  • その際、「例外」に至るまでの「関数呼び出し」の履歴と、エラー内容が画面表示されます。

その他の言語仕様について

以上、第1回の講座では、pythonの言語仕様について、C言語と比較する形で簡単にご紹介しました。
実際にpythonでプログラムを作成する上では、上記以外にも、知っておいた方が良い事柄が沢山あります。以下では思い付いた事柄を列挙します。詳細については、ネット検索でご確認ください。

  • 日本語の文字エンコードは、デフォルトはUFT-8。これをシフトJISに変更したい場合は、ソースファイル先頭にコメントとして、以下を記述すれば良いです。

# -*- coding: sjis -*-

  • pythonではfor文以外にも、if文、elif文、else文、while文 の制御文が使用できます。
  • switch~case文に相当する制御文として、Pythonの3.10以降ではmatch~case文が使用できるようになりました。
  • ただ古いpythonでも実行できるソースコードを書くならmatch~case文は使わずに、if,elif,else文を並べて、同じ変数値を繰り返し比較する必要があります。

  • 関数の先頭行や、if文、elif文、else文、while文 の末尾には、コロン(:)が必要です。
  • これを忘れると、実行時にエラー表示され、実行できません。

  • ループ文のブロック内では、continue文、break文が使用できます。
  • ブール値(true/false)には、大文字から始まる True、False が使用できます。
  • C言語の && || は、and or と記述する必要があります。
  • インクリメント、デクリメントは使用できません。代替として、+= や -= を使用します。
  • #define は使えません。代替としては、大文字で変数を宣言し、通常の変数のように初期化して使用する記述が良く使われます。
  • #ifも使えません。筆者は代替として通常のif文を使っています。例:「#if 0」は「if (False)」等
  • 豊富なライブラリが用意されています。pythonのインストール時にインストールされなかったライブラリは、DOSプロンプト上で実行する「pip」コマンドでいつでも追加でインストールできます。
  • 「こんなライブラリが欲しい」と思われた時点で、ネット検索で確認してください。お目当てのライブラリが見つかると思います。

C言語による構造化/階層化設計 入門講座(第3回)

本講座のトップページでは、C言語仕様が自由である事の功罪と、特定の個人/組織でしか通用しないローカルなルールでソフトウェア設計を束縛する事の功罪について述べました。

以上の事を踏まえ、第3回では、オブジェクト指向設計の世界で常識とされている幾つかの習慣をC言語によるソフトウェア設計に取り入れる事を提案します。今回提案する習慣は、以下の5つです。

  1. 変数と関数の有効範囲(スコープ)を局所化する。
  2. グローバル関数の呼び出しを「ソフトウェア部品名_アクション名(引数…)」で表現する。
  3. 初期化関数を持つ。
  4. ソフトウェアの構造を階層化する。
  5. 上位アプリケーションをパソコン上で動かす。

変数と関数の有効範囲(スコープ)を局所化する

第1回では「特定の役割り」を担う複数の「部品」によってソフトウェア全体を構成する事の重要性について述べ、その部品が備えるべき特性として、他の「部品」からは見えない/見る必要の無い、隠された「変数」や「関数」を有するという特性を挙げました。これは、オブジェクト指向設計において、当たり前のように適用されている習慣です。

この習慣を適用するための第一歩は、「グローバル変数」を極力、使わない事です。重要なデータは、何れかのソフトウェア部品内でstatic宣言して外部から隠ぺいし、データを所有するソフトウェア部品によって許可された方法、すなわちグローバル関数呼び出しによってのみ、更新または参照できるようにするのです。

これにより、データ構造も隠ぺいできます。たとえば「0から始まるユニークな番号」と、「16ビット長のデータ」を「対」で沢山、保持するソフトウェア部品があったとします。この場合、データは以下のような構造体配列で管理できます。

#include <stdint.h>

#include <stdbool.h>

 

#define DF_XXX_DATA_NUM  1000

 

typedef struct {

uint16_t no;

uint16_t data;

} ST_XxxDataType;

 

static ST_XxxDataType lv_XxxDataTable[DF_XX_DATA_NUM];

がしかし、このデータを参照する側のソフトウェア部品では、このようなデータ構造の詳細は知る必要が無く、参照用の関数として用意された、以下のようなグローバル関数を呼べば良いだけです。

#include <stdint.h>

#include <stdbool.h>

 

extern bool DB_ReadData(uint16_t no, uint16_t *pData);

第1引数の番号に相当するデータが存在しない場合はfalseを返し、存在する場合は、第2引数のポインタが指す先にデータを格納し、trueを返せば良いのです。

こうすれば、今後、何かの事情でデータ構造を変更する必要が生じた場合でも、データを参照する外部のソフトウェア部品のソースコードは変更しなくて済みます。またデバッグの目的でデータ参照のログを取りたい…という場合は、前記のグローバル関数の中でログを出力するだけで済みます。更に外部のソフトウェア部品のグローバル関数を「スタブ」と呼ばれるテスト用の関数に差し替える事で、単体テストがし易くなります。

このように設計する事に対して、関数呼び出しが1段増え、実行時間やコードサイズが増える事を嫌がる人がいますが、本講座のトップページで述べたとおり、昨今のマイクロプロセッサの高速化/フラッシュメモリの大容量化/低価格化を考えれば、許容されるレベルと思います。

関数についても、グローバル関数を減らす事を心掛け、外部から呼ぶ事のできないstaticな関数を中心に処理します。

グローバル関数の呼び出しを「ソフトウェア部品名_アクション名()」で表現する

オブジェクト指向設計では、本記事で提案する「ソフトウェア部品」は「オブジェクト(インスタンスと呼ばれる場合もあります)」に相当します。クラスは、オブジェクトの「型」を意味します。

そのオブジェクト指向設計では、オブジェクトが所有する関数の内、外部のオブジェクトから呼び出せる関数は「publicなメンバ関数」と呼ばれ、その関数呼び出しは「オブジェクト名.関数名()」で表現します。オブジェクト指向設計では、publicなメンバ関数呼び出しは、そのオブジェクトに対して「メッセージ」を送る行為と見なせます(「メッセージパッシング」といいます)。たとえばデータベースの役割りを担うオブジェクト「dataBase」に対して「データを読みたい」というメッセージを送るには、「dataBase.Read()」という関数呼び出しを実行するのです。

このようにオブジェクト指向設計では、ソフトウェア部品間で何らかのメッセージやデータ(メンバ関数の引数で渡す)をやり取りする場合、必ず「dataBase.Read()」のように

  • ソフトウェア部品名(すなわちオブジェクト名)」
  • メッセージ内容(すなわちpublicメンバ関数名)

を「ペア」で表現する事がプログラミング言語仕様で義務付けられています。

これに対して、C言語ではグローバル関数の呼び出しに際して、そのようなルールは存在しません。従って、本記事で提案する「幾つかのソフトウェア部品で全体を構成する」ような設計を行い、その部品間でやり取りする際、どの部品に対するアクションなのか、Cの言語仕様としては、表現する手段が無いのです。

ではどうすれば良いでしょうか?

本記事では、グローバル関数名に特別な「命名規則」を設ける事で、この問題に対処する事を提案します。具体的には、「グローバル関数名のプリフィックス(接頭語)としてソフトウェア部品名を書く」事を提案します。更に、1つのソフトウェア部品は「部品名.c」と「部品名.h」の2つのソースファイルに記述する事を提案します。ヘッダには、そのソフトウェア部品のグローバル関数呼び出しに必要な「プロトタイプ宣言」と「型宣言」「マクロ宣言」等を記述します。

たとえばデータベースの役割りを担うソフトウェア部品をCのソースファイルで「DataBase.c」「DataBase.h」として記述したとします。この場合、ソフトウェア部品名は「DataBase」になりますので、そのグローバル関数名は、たとえば「DataBase_Read()」とします。

ただしこの命名規則だけでは、static関数名と区別できないので、

  • グローバル関数は、大文字で始め、static関数は小文字で始める。
  • static関数には、プリフィックス(接頭語)として「l_」を付ける。
  • static関数名には、アンダースコア「_」は使わない。

等、static関数にも「命名規則」を設ける必要があります。

以上のようなルールは、特定の個人や組織でしか通用しないルールなのですが、その根本は、オブジェクト指向設計のルールに基づいており、効果的なルールとして機能すると思います。

初期化関数を持つ

オブジェクト指向設計では、オブジェクトを動作させる際、予め「生成」というステップを踏む必要があるのですが、その際、「コンストラクタ」と呼ばれる、自身を初期化するための専用のメンバ関数が呼ばます。C言語によるソフトウェア設計でも、この習慣を取り入れる事を提案します。具体的には、「ソフトウェア部品名_Init()」という初期化関数を準備し、起動後にSchedulerから1度だけ呼び出すようにするのです。

ソフトウェアの構造を階層化する

C言語において、変数/関数の有効範囲(スコープ)として利用できる範囲は、以下の3種類しかありません。

  • ソースファイル内(すなわちコンパイル単位)
  • 関数内
  • { }で囲まれたブロック内

従って、これらを有効活用してソフトウェアを設計する事になるのですが、理想的な有効範囲は、それぞれ

  • 1ソースファイルは、千行以内
  • 1関数は、数十行くらい
  • 1ブロックは、数行くらい

だと思いますので、大規模なソフトウェア開発では、千行くらいのソースファイルを大量に書く事になります。

変数や関数に局所性を持たせたのと同じ理由で、ソフトウェア部品についても、全ての部品と関係を持つソフトウェア部品は少なくし、一部のソフトウェア部品間でのみ関係を持つように、局所化/グループ化する事をお勧めします。その際、同一グループのソースファイル群は、サブフォルダに分けて管理する事をお勧めします。

そのソフトウェア部品の局所化/グループ化に際しては、1階層の広い平面内で局所化/グループ化するだけで無く、多層化し、階層化された空間内で局所化/グループ化する事も重要です。

この階層化ですが、オブジェクト指向設計では、オブジェクト間で「継承(is-a)」や「包含(has-a)」という関係を持たせ、階層化する事が一般的ですが、C言語では、クラスが無いため、そのような設計を行う事は難しいです。(関数ポインタをメンバに持つ構造体を使えば、同じような設計ができなくは無いですが、本記事で提案したい方法ではありません)。ただ「オブジェクト指向設計では、階層化する事が一般的なのだ」という事を念頭に置いて、C言語によるソフトウェア部品の設計に際しても、階層化を意識する事は重要と考えます。

では、どのようにソフトウェア部品を階層化すれば良いでしょうか?

その1つのアイデアは、「詳細な仕事」を担うソフトウェア部品と、その機能を活用して「より抽象的な仕事」を担うソフトウェア部品を設ける事で、階層化する案があります。第2回の「擬似パソコン化」では、

  • Device Driver
  • Hardware Abstraction Layer

というソフトウェア部品について説明しましたが、これらは階層化された関係を持つと言えます。

上位アプリケーションをパソコン上で動かす

上位アプリケーションをパソコン上で動かす事は、オブジェクト指向設計とは直接、関係しないのですが、オブジェクト指向で設計されたソフトウェアの多くは、WindowsやLinuxが稼働するパソコン上で実行されるため、ここで説明させて頂きます。

これまで述べてきたような習慣に基づいてソフトウェアを設計した場合、階層化された設計の上位に位置するアプリケーション部は、実際のハードウェア上だけで無く、パソコン上で動かす事も容易です。この記事では、上位に位置するソフトウェア部品をWindowsやLinux上のソフトウェアとして開発される事、もしくは実際のハードウェア上でデバッグされる前に、上位に位置するソフトウェア部品だけをパソコン上で実行される事(デバッグされる事)をお勧めします。この設計方法は、一部の書籍では「デュアルターゲット」として紹介されています。

パソコン上のソフトウェアとして組み込みソフトウェアの一部を開発した場合、以下のようなメリットが得られます。

ハードウェアを入手するまでに上位アプリケーション部の単体テストが実施できる

昨今の組み込みシステムの開発では、ソフトウェアとハードウェアは同時並行開発される事が多いです。更に、ソフトウェア設計者にハードウェアが手渡される際、デバッグやテストを終えた信頼性の高いハードウェアが手渡される事は稀で、ソフトウェアのデバッグの際に、併せてハードウェアの動作検証を求められる事は少なくありません。

こういうケースでは、いきなり上位アプリケーションまで含めた全ソフトウェアをハードウェア上で動かすと、不正動作の原因が解らず、混乱を招く事が多いです。

これに対して、上位アプリケーション部は、別途、パソコン上で開発/デバッグ/単体テストまで進めておき、入手したハードウェア上では、本記事で「擬似パソコン化」として提案した一部のソフトウェア部品と、その上位に位置づけられる「試験用ソフト」だけを動作させ、別々に開発を進めた場合、前記のような混乱は、最小限に抑えられます。

高度なデバッグ機能が利用できる

たとえばVisual Studio上で開発した場合(Visual C++を利用)、未初期化のauto変数を参照したり、不正なメモリをアクセスした場合は例外が発生し、ブレークする事が多いため、この種の不具合は、初期段階で改修できる可能性が高いです。これに対して、前記のような不具合を含むソフトウェアを実機上で実行した場合、不正アクセスの直後でブレークさせる事は難しく、別の異常現象として現れますので、デバッグに時間を要する事になります。

組み込みソフトウェア技術者「以外」の技術者に協力を仰げる

これまで述べてきたような習慣に基づく上位アプリケーション部の開発は、組み込み用途で無い、一般的なソフトウェア開発と大きな違いはありません。よって、技術者を確保する際、組み込みソフトウェア技術者だけで無く、C言語特有の言語仕様(ポインタ等)を勉強してもらう前提で、C#/Java等の経験者に協力を仰ぐ事も可能になります。

ダウンロードが不要

パソコン上でソフトウェアを直接(ネイティブとして)実行する場合、ダウンロードという手順は不要で、ビルドが終われば、即、実行可能です。これに対して、組み込みソフトウェアを実機上で実行するには、ダウンロードという手順が必要となります。このダウンロードですが、累計時間で見れば、それ程、負荷の高い作業では無いですが、別の作業が実施できる程の時間では無く、思考が途絶える「待ち時間」になってしまう事が多いです。故に、ビルド時間も短く、ダウンロードが不要なソフトウェア開発は、設計者にとって生産性が格段に向上したような感覚をもたらします。

第3回は以上です。続く第4回では、これまで述べた習慣に基づき、実際に「電気ポット」のソフトウェアを設計します。

C言語による構造化/階層化設計 入門講座(第2回)

第2回では、Schedulerについて説明します。Schedulerは、第1回の「擬人化」「擬似パソコン化」の説明の中で、最後に登場したソフトウェア部品です。

Schedulerは、ソフトウェア部品が有するグローバル関数の内、並列実行の対象となる関数群のスケジューリング(実行順序制御)を担います。

グローバル関数が呼ばれるタイミング

各ソフトウェア部品は、それぞれ幾つかのグローバル関数を有しますが、それらのグローバル関数が呼ばれるタイミングは、以下の3種類に分類できます。

  1. 他のソフトウェア部品から直接呼ばれる。
    他のソフトウェア部品に対して、入出力、表示、生成等の機能を提供するグローバル関数は、その機能が必要になったタイミングでそれを必要とする他のソフトウェア部品から直接、呼ばれます。
  2. Event Generatorのコールバック関数として呼ばれる。
    ハードウェアの入出力、変換等、状態が変化したタイミングでEvent Generatorから呼ばれます。
  3. 並列処理関数として呼ばれる。
    他のソフトウェア部品と並列に実行しなければならない関数は、Schedulerと呼ばれるソフトウェア部品から呼ぶようにします。

この記事では、並列処理関数の設計手法である「ステートマシン」について説明した後、上記3のSchedulerによるスケジューリングについて説明します。

なお本記事では、リアルタイムOSなしでスケジューリングを行う事を想定しています。

並列処理を実現するための留意点

組み込みシステムでは、前記1~2のいずれの場合でも、1つの関数でCPU実行権を長時間、占有する事は許されません。なぜなら、そういう関数を作ってしまうと、並列動作させるべき他の処理が実行できないからです。

1回あたりの関数呼び出しで許容されるCPU占有時間(すなわち実行時間)は、開発するシステムの要件に依存します。故に、一概に「XXmsec以内」とは言えませんが、たとえばイベント発生待ちや、大量の計算/検索のために、実行時間の長い繰り返し処理を1回の関数呼び出しで処理する事は避けた方が良いと思います。

では、どうすれば良いでしょうか? この記事では、並列処理関数の設計手法として「ステートマシン」を紹介します。

ステートマシンとは

ステートマシンは、組み込みソフトウェアでは一般的な設計手法です。オブジェクト指向設計のデザインパターンでは「ステートパターン」に相当します。関数が呼ばれた際、毎回、同じ処理を実行するのでは無く、現在の「状態」に応じて異なる処理を実行するように設計します。

たとえばある処理を行う過程で、イベント発生を待たなければならないとします。この場合、関数の中の while文でイベント発生を待ってしまうと、目的のイベントが発生するまで、CPU実行権を占有してしまいます。この問題を回避するため、たとえば「前処理」「イベント発生待ち」「メイン処理」の3つのグローバル関数に分割し、Schedulerに呼び分けてもらう案も考えられますが、この方法では、ソフトウェア部品の内部仕様を外部にさらす事になり、更にグローバル関数の数も増えるため、望ましくありません。

こういうケースでは、ステートマシンの設計手法を利用します。イベント待ちが必要ならば「初期状態」「イベント待ち状態」の2つの状態を持つようにします。この「状態」を保持する変数は、enum型のstatic変数として定義します。

typedef enum {

e_Init = 0,

e_WaitEvent

} EN_State;

static EN_State lv_state = e_Init;  /* 関数の状態種別 */

関数の実装では、swtich~case文で、現在の状態に応じて、異なる処理を実行するようにします。

void 並列処理_その1(void)

{

switch(lv_state) {

case e_Init:

前処理();

lv_state = e_WaitEvent;

break;

case e_WaitEvent:

if (イベント発生チェック() == true) {  /* イベント発生済かチェック */

メイン処理();             /* イベントに応じたメイン処理 */

lv_state = e_Init;         /* 次回の呼び出しに備えて、状態を戻す */

}

else {

;           /* イベント未発生なら何もせずに直ぐに戻る */

}

break;

defalut:

内部エラー処理();      /* あり得ない(内部エラー処理) */

lv_state = e_Init;

break;

}

}

イベント発生は、上記の関数呼び出しとは非同期に、Event Generatorによってコールバック関数で通知してもらうようにしておき、その情報は、static変数に保存しておきます。イベント発生チェック()では、コールバック関数で通知され、static変数に保存された直近のイベント情報をチェックするようにします。

このように設計すれば、このソフトウェア部品と外部のソフトウェア部品間のインタフェースは、イベント通知のコールバック関数と、並列処理関数の2つに集約でき、更にこのソフトウェア部品がどういう状態を持つかは外部から隠ぺいできます。

Schedulerによるスケジューリング

前記のとおり、並列処理関数をステートマシンで設計した後、Schedulerと呼ばれるソフトウェア部品から、各並列処理関数を呼び出します。

その際の最も単純なスケジューリングの方法は、ラウンドロビンと呼ばれる方法です。ラウンドロビンでは、並列処理に相当するグローバル関数を単純に順番に、繰り返し呼び出します。たとえば、以下のような感じです。

void Scheduler_main(void)

{

while (true) {

並列処理_その1();

並列処理_その2();

並列処理_その3();

並列処理_その4();

}

}

上記関数は、関数mainから呼ばれる無限ループの関数です。これが最も単純なスケジューリングの方法です。

この方法に対して更に、イベントAが発生した場合は「並列処理_その1()」と「並列処理_その2()」は休止させて、代わりに「並列処理_その5()」を実行し、その処理が終わり次第、元の状態に戻すというようなスケジューリングを追加する事も可能です。この場合、Scheduler内のスケジューリング関数Scheduler_main自体をステートマシン化し、以下のように設計します。

typedef enum {

e_Normal = 0,

e_Emergency

} EN_State;

static EN_State lv_state = e_Normal;  /* 関数の状態種別 */

 

void Scheduler_main(void)

{

while (true) {

bool isComplete = false;

 

/* イベント発生チェック */

if (lv_state == e_Normal) {

if (イベントAの発生チェック() == true) {

lv_state = e_Emergency;

}

}

swtich (lv_state) {

case e_Normal:

並列処理_その1();    /* 定常時の処理 */

並列処理_その2();

break;

case e_Emergency:

isComplete = 並列処理_その5();   /* 緊急時の処理 */

if (isComplete == true) {

lv_state = e_Normal;     /* 緊急処理が完了したなら状態を戻す */

}

break;

}

並列処理_その3();  /* 定常時/緊急時 共に実行すべき処理 */

並列処理_その4();

}

}

上記の実装例では、イベントAの発生後、緊急処理の「並列処理_その5()」が開始されるまでの待ち時間は、最大で「並列処理_その1()」~「並列処理_その4()」の最大時間の「合計」になります。この待ち時間が長すぎる場合は、関数Scheduler_mainの状態管理を詳細化する事で、「並列処理_その1()」~「並列処理_その4()」の中の「最大時間」まで短縮できます。

以上のように、ステートマシンとSchedulerの設計手法を利用する事で、リアルタイムOSなしでも、ソフトウェア部品の独立性を維持しつつ、リアルタイム性を確保した設計が可能になります。

第2回はここまでです。次回からは、本記事で提案する以下の2つの習慣

  1. 「特定の役割り」を担う「部品」によってソフトウェア全体を構成する。
  2. オブジェクト指向プログラミングの習慣を取り入れる。

のうち、後者の「オブジェクト指向プログラミングの習慣を取り入れる」について説明します。

参考資料:ステートマシンによる並列プログラミング入門

C言語による構造化/階層化設計 入門講座(第1回)

第1回では、本講座で提案する「2つの習慣」

  1. 「特定の役割り」を担う「部品」によってソフトウェア全体を構成する。
  2. オブジェクト指向プログラミングの習慣を取り入れる。

のうち、1について解説します。このページでは、以下の内容について説明しています。

「特定の役割り」を担う「ソフトウェア部品」とは?

「特定の役割り」を担う「ソフトウェア部品」とは、たとえば以下のような「特性」を備えたC言語の「モジュール」、すなわちコンパイル/リンク単位です。

  • 他の「部品」は担わない、その部品ならではの「役割り」を担う。
  • 他の「部品」からは見えない/見る必要の無い、隠された「変数」や「関数」を有する。
  • 「部品」間は「グローバル関数呼び出し」によって情報交換(すなわち入出力)を行う。

グローバル変数は、基本、NGとしたいですが、グローバル変数を使わない代わりに、Getter/Setterと呼ばれる種のグローバル関数を乱用する事もNGとしたいです。システムを分析し、要件を満たす最適な部品と、その組み合わせを見つけ出した結果、グローバル変数の代わりに、少ないGetter/Setter関数を用意すれば済む…を目指します。

オブジェクト指向設計の用語を使って説明しますと、唯一のインスタンスしか生成できない(Singletonな)クラスを使ってソフトウェアを設計するイメージです。

ではどうやって、上記で説明したような、システムにおける最適な「部品」の組み合わせを見つけ出せば良いでしょうか?この記事では、そのためのアイデアとして「擬人化」「擬似パソコン化」という観点を提案します。

擬人化

「擬人化」では、たとえば我々の目の前に複雑で難解な仕事があった場合に、複数のメンバで協力し、各メンバはそれぞれ単純で解り易い仕事を担い、メンバ全員で協力して協調的に仕事する時のような発想で、役割りを決めます。たとえば「Watcher(見張り人)」「Generator(生成人)」「Converter(変換人)」「Viewer(表示人)」「Manager(管理人)」のような役割りを設けます。

  • Watcher(見張り人)は、何かを監視し、その結果を周りに伝える役割りを担います。
  • Generator(生成人)は、何かを生成し、その結果を周りに提供する役割りを担います。
  • Converter(変換人)は、何かから別の何かを生成し、その結果を周りに提供する役割りを担います。
  • Viewer(表示人)は、何かを表示する役割りを担います。
  • Manager(管理人)は、全体をまとめる役割りを担います。

こういうような役割りを設け、モジュール名として「xxxWatcher.c/.h」というよう名前を付ければ、仕様書を読まなくても「このモジュールはxxxの監視を担うモジュールだな」と、大よその推測ができます。

擬似パソコン化

Webアプリやゲームソフトは、予め決められた環境下で動作します。たとえばパソコン上のWebブラウザや、Androidスマホ上などです。これらの環境下でソフトウェアを開発する場合は、GUIボタン表示、そのボタンがクリックされた際のイベント発生等、殆どのソフトウェアが同様に必要とするようなソフトウェア部品は予め準備されており、プログラマは、それらを最初から利用できます。

しかし組み込みソフトウェアにおいては、そのような標準的な動作環境は存在せず、システム固有の入出力装置を備えたハードウェア上で動作させる必要があります。OS(オペレーティングシステム)も、プロジェクトの要件によって、使用できる場合と、使用できない場合があります。

このように、標準の動作環境が無い組み込みソフトウェアですが、そのソフトウェア開発において「まず動作環境から作成する」というようなステップが取られる事は稀です。ですので、たとえばアプリケーション処理から、直接、低レベルの入出力のためのレジスタを制御してしまう…というような事が行われがちです。このようなソフトウェアを開発してしまうと、その後、ソースコードを別のマイコン上で動作するように移植せねばならない…というような流用開発の時は、アプリケーション中に点在する入出力処理を書き換えないといけなくなります。

「擬似パソコン化」では、アプリケーションソフトが動作する「動作環境」を「特定の役割り」と位置づけ、部品化します。「擬似パソコン」は、たとえば以下のような部品(モジュール)で構成します。

  • Device Driver
  • Hardware Abstraction Layer
  • Event Generator
  • Scheduler

Device Driver とHardware Abstraction Layer

Device Driver(以下Driverと記す)は、入出力ポート、シリアルI/O、A/D変換器等、マイコンの周辺ハードウェア(Peripheralといいます)を制御する役割を担います。

またHardware Abstraction Layer (以下Halと記す)は、Driver内のグローバル関数を呼び出し、上位アプリケーションに対してアプリケーション固有の入出力機能を提供します。

DriverとHalの違いですが、たとえばDriverは入力ポート0番の第1ビット目から入力する 等、マイコン周辺のハードウェアに強く依存した機能を提供するのに対して、Halは周辺ハードウェアの更に先に接続されたハードウェアを制御する事に着目している点が違います。たとえば温度センサを扱うシステムの場合、上位アプリケーションは、Halとして準備された関数Hal_GetTemprature()を呼ぶ事になります。これを受けてHalでは、温度センサがシリアルI/Oで繋がっているシステムならば、Drv_ReadSio(uint8_t ch)のようなDriver関数を呼ぶ事になります。

このような役割分担にする事で、上位アプリケーションは、温度センサをA/D変換器で制御するのか、シリアルI/Oで制御するのか、意識する必要が無くなり、Halに委ねる事ができます。

Event Generator

Event Generatorは、ハードウェアの状態変化を検出し、該当するコールバック関数を呼び出す役割を担います。

パソコンやスマホでは、マウスやタッチパッドを操作した際、該当するコールバック関数が呼ばれます。たとえばマスクがクリックされた際は、関数OnClickが呼ばれる…という感じです。これと同じ要領で、組み込みシステムの周辺ハードウェアの状態変化をコールバック関数を呼び出す方法で上位アプリケーションに通知します。

Event Generatorは、たとえば「スタートボタンは1秒間、長押しされた際に検出される」というようなアプリケーション固有の要件があった場合、その要件に従って状態変化を検出します。こういう役割分担にする事で、上位アプリケーションは、状態変化の検出はEvent Generatorに任せ、それ以外の仕事に専念できるようになります。

Event Generatorは、ハードウェアに依存した処理を行いますので、Halと同じく、Driver関数を直接呼び出してもかまいません。

Event Generatorのコールバック関数を登録する方法としては、コールバック関数を登録するためのグローバル関数をEvent Generator側に用意し、他のソフトウェア部品から登録関数を呼ぶ際、呼び出して欲しいstatic関数のポインタを引数として渡す方法が考えられます。この場合、Event Generatorは、ポインタ間接でstatic関数を呼び出す事になります。

もしくはEvent Generatorの仕様書に「xxxイベント検出時は、zzzモジュール内のグローバル関数yyyを呼び出す事」というような仕様を記載しておき、そのとおりに実装する方法も考えられます。この2つの方法のいずれの場合でも、関数名の命名において、その関数は何のイベントが発生した際に呼ばれるコールバック関数であるか、第三者に解るよう配慮しておく事をお勧めします。たとえばcallback_StartButton()のような感じです。

第1回はここまでです。「特定の役割り」を担う「ソフトウェア部品」を選定するために、「擬人化」と「擬似パソコン化」の2つの観点で役割分担を検討してみました。
次回は、組み込みシステムにおいて極めて重要な「Scheduler」について説明します。

「福のラジオ」から紐解く 福山雅治さんの魅力

福山雅治さんについて書きたいと思います。

音楽アーチストとしても俳優としても、非凡な活躍をされている福山さんですが、TOKYO FM「福のラジオ」から伝わってくる福山さんのイメージは、「頂点」「完成」「勝利者」等とは対極にある、「新鮮」かつ「発展途上」のイメージなのです。

驚異のFMラジオ番組「福のラジオ」

私は、TOKYO FMから全国ネットで放送されているFMラジオ番組「福のラジオ」の熱心なファンで、毎週土曜の14時から欠かさず拝聴しているのですが、色んな意味でこの番組は凄い番組だと思います。

卓越した福山さんの話術

何が凄いかと申しますと、福山さんの話術です。面白いのです。「しゃべりの師匠は(オールナイトニッポンの笑福亭)鶴光師匠」と公言されているだけの事はあると思います。番組の構成作家であられる今浪祐介(いまなみゆうすけ)さんとの掛け合いトークは、「ガキ使」でダウンタウンさんが展開される即興漫才のような「話芸」の域に達していると思います。

流れとしては、毎度、今浪さんの不用意な発言に対して、福山さんが絶妙なつっこみを入れられる展開です。今浪さんも受け役としていい味を出されています。パワハラ感の漂う福山さんの鋭いつっこみに耐え忍ぶ今浪さんを愛おしく感じる時があります。

常識を超えた番組構成

次に凄いのは、番組の構成です。
番組中の多くの時間は、福山さん作品(音楽/ドラマ/映画等)のプロモーションに使われているのです。この種の構成は、一歩間違えると、福山さんの積極的なファンで無い方々がラジオを点けられた際に拒否反応を示されるリスクがあると思うのですが、「福山さん作品のプロモーション活動」という「舞台」を通して、福山さんと今浪さんの絶妙なトークが展開されますので、福山さんのファンで無い方々も充分に楽しめます。

結果、トークを楽しみに毎週拝聴する中で、気付いたときには福山さん作品のファンになってしまっている…という事が起こり得ます。何しろ、私自身がそうだったのです。これは、プロモーションの手法として革命的な手法では無いか? と思ったりします。

爆笑の視聴者お便りコーナー

また「ふつおた(普通のお便りの意)」「コーポの女」「主婦川柳」「恋王」他、視聴者からのメールを紹介するコーナーが設けられていますが、これらのコーナーでは、土曜のお昼のFMラジオ番組とは思えない、超爆笑な、しかもエロい内容が展開されます。そのエロい内容を男前の福山さんが、顔を見せずにサラッと言い切られるので、いやらしく感じません。

「福のラジオ」から垣間見える 福山雅治さんの『人間性』

同番組の中では、福山さんの人間性が垣間見えるトークも展開されます。

消費者目線を忘れない福山さん

印象に残っているのは、福山さんがスタッフの皆さんと昼食を摂られる時のエピソードです。
印税その他で多額の資産をお持ちのはずの福山さんですが、数種のランチメニューの中から、高いメニューを注文される事は無く、手頃な価格のメニューを選択されるそうです。この点について、今浪さんが「なぜですか?」と問われた際、福山さんは「ランチメニューは、殆どの場合、安いメニューの方がコストパフォーマンスとして高いメニューより優れているから」との事でした。要は、折角、高いお金を払って高いメニューを注文しても、それに見合う満足感が得られないとガッカリしてしまうので、それが嫌なのだそうです。

巨額の富を得ながら、我々消費者と同じ金銭感覚を維持されている事に驚きましたし、また業種は違えど同じ「表現者」と言える「料理人」に対して妥協されない姿勢は素晴らしいと思いました。

また幼少の事、ご実家は裕福で無かった事も度々公言されています。ただ今浪さんが「子供の頃、ハンバーグを食べる事も無かったんですよね」と問われると、福山さんは「そこまで貧乏じゃ無かったよ!」と憤慨され、今浪さんが恐縮される場面もありました。

ご自分を「美しい」と感じている福山さん(?)

旅先のエピソードとして、福山さんはホテルへのチェックイン後、即シャワーを浴びられる事を今浪さんが紹介されていました。「常に美しい/清潔な福山さんを維持したいからなんですよね?」と今浪さんにつっこまれると、福山さんは憤慨され「今浪さんは、旅館に行った際、先に風呂に入るのか、先に食事を摂るのか、どっちなんだ?」と問われ、今浪さんが「風呂です」と答えられると、「やっぱそうだろ! それと同じ気持ちなんだ。サッパリしてから食事を楽しみたいだけなんだ」と主張されていました。
こういう、どうでもいい事にも向きになられる福山さんは、微笑ましいです。

レジェンドへのリスペクトを忘れない福山さん

番組では、泉谷しげるさん、Charさん、鮎川誠さん等、音楽界のレジェンドへのリスペクトの気持ちを度々述べられます。その謙虚さ、更なる上を目指される貪欲さは、素晴らしいと思います。
その中のエピソードとして、山下達郎さんがご自身のFMラジオ番組「サタデーソングブック」で福山さんのカバーアルバム「魂リク」から「元気をだして」をオンエアされたときのエピソードが印象に残っています。仕事の移動中、車内での出来事だったらしいのですが、オンエアされてる事に気付いた福山さんは、スタッフさんに車を脇に停車するよう要求され、達郎さんのトーク/批評に真剣に耳を傾けられ、オンエアされた事にいたく感動されていました。「あの山下達郎さんが『すごい』ってオンエアして下さったんだよ!」と、子供のように感激されていた事を覚えています。
いつまでも新鮮さを失わない理由の1つが垣間見えた場面でした。

BL(ボーイズラブ)疑惑の福山さん(?)

昔、同番組には「主婦『雅子』」のコーナーがありました。福山さんが主婦「雅子」に扮して、視聴者からのメールに主婦目線でコメントされるコーナーで、「おねえ言葉」で話されていました。そのせいでしょうか、それ以外のコーナーでも全体的に「おねえ言葉」が多く、結婚されない事もあり、福山さんは実は男性がお好きなのでは?? という疑惑がありました。
その後、ご結婚され、一子も授けられたとの事で、この疑惑は払拭された…と思いきや、再び同番組にて、香川照之さん、斎藤工さんとのBL(ボーイズラブ)疑惑が浮上しています。
いつも興味深い話題を提供してくれる番組です。

以上、「福のラジオ」から紐解く 福山雅治さんの魅力をご紹介しました。
いつまでも発展途上、チャレンジャー、未完成である故に、最強の表現者であられる福山雅治さんを今後も応援します。

VBAによるプログラミング超入門講座(第4回)

今回は、Excelに付属するソフト「Visual Basic Editor」を使って、演習問題の解答例を実行する手順について説明します。

このページでは、以下の内容について説明しています。

Visual Basic Editorの使用目的

Visual Basic Editorは、以下の目的で使用します。

  1. プログラミング言語VBAで書いたソースプログラムをExcelに入力するため。
    本講座の第1回~第3回を読まれた方は、演習問題を解く際、紙と鉛筆を使ってソースプログラムを書かれたと思いますが、その状態では作成したプログラムは実行できません。Visual Basic Editorの画面から作成したソースプログラムを入力する事で、コンピューターおよびExcelに対して、実行して欲しいプログラム(すなわち作業指示書)の詳細を伝える事ができます。
  2. プログラムの記述誤りをチェックするため。
    Visual Basic Editorにソースプログラムを入力すると、単純な記述誤りを発見してくれます。
  3. 入力したソースプログラムを機械語のプログラムに翻訳しながら実行するため。
  4. 実行中のプログラムを途中で止めるため。
  5. 途中で止めたプログラムを続きから1行ずつ実行するため。
  6. 変数に記憶している値を確認するため。

上記の内、4~6は、作成したプログラムが期待どおりに動作せず、プログラムの誤りを見つけたいときに行う作業です。これらの作業のことを「デバッグ」といいます。

Excelの初期設定

Visual Basic Editorを使用するには、予めExcel側で必要な初期設定を行う必要があります。その手順を以下に示します。ここではExcelのバージョン「Excel 2010」を使用するケースを想定しています。

「開発」タブを追加する。

Excelの上部のタブの並びから、左端の「ファイル」タブを選択し(下図の①)、表示された画面から左端の下の「オプション」ボタン(下図の②)をクリックします。

pic_3

すると、新たなダイアログが表示されます。初期状態では、左端の一番上の「基本設定」が選択されていますので、左端の列の途中「リボンのユーザー設定」(下図の③)を選択して、下図の画面を表示させて下さい。その画面の右端の表示をスクロールさせて「開発」のチェックボックスを表示させて下さい(下図の④)。このチェックボックスが選択されていない場合は、選択して、OKボタンをクリックして下さい。

pic_4

結果、Excelの初期画面のタブに「開発」タブが表示されます(下図の⑤)ので、これをクリックして下さい。

pic_5

上図のような画面に変わります。この画面のリボンから、右端の「Visual Basic」(下図の⑥)をクリックして下さい。Visual Basic Editorが起動できます。

pic_1

なおVisual Basic Editorは、Excelを開いた状態で「Alt」キー(キーボードの左下付近にあります)とF11キー(キーボードの上側にあります )を「同時」に押す方法でも起動できます。

Visual Basic Editorの画面が開いたら、「表示」⇒「ツールバー」メニューの「標準」と「デバッグ」を選択しておいて下さい。

お勧めの追加設定

前節までの設定を行えば、VBAでプログラミングを始める事ができますが、追加で以下を設定される事をお勧めします。

プログラム入力途中に構文チェック結果をダイアログで表示させない

実際にVisual Basic Editorを使ってプログラムを入力し始めると、不完全なプログラムだとして、再三、ダイアログでエラーメッセージが表示されます。入力途中なのでプログラムが不完全である事は自分で解っているので、毎度、OKボタンでダイアログをクローズしないといけないのは面倒です。このエラー表示は、Visual Basic Editorの設定を変更する事で抑止できます。抑止した場合は、ダイアログは表示されなくなり、プログラムの入力画面上で、誤った行が赤字で表示されるようになります。

変数の宣言が無い場合にエラーを表示させる

本講座では、変数を使用するときは事前に「Dim」文で宣言する事を推奨しています。ですが、Excelではインストール直後は、変数の宣言なしに、いきなり変数を使って良い設定になっています。これは、プログラミングに慣れた人にとっては便利な機能ですが、プログラミング入門者にとっては、変数名を誤って入力してしまった場合にエラーが表示されないので、間違い探しに時間を要する危険性があります。

この問題を避けるには、プログラムの先頭で「Option Explicit」と宣言すれば良いです。これにより、作成するプログラムで宣言なしで変数を使用した場合、エラーが表示されるようになります。この「Option Explicit」を自分で書くのが面倒な場合は、Visual Basic Editorで事前に設定しておけば、自動的にこの行が宣言されます。

Visual Basic Editorに対して、上記の2つの設定を行うには、Visual Basic Editorの「ツール」⇒「オプション」メニュー (下図の⑦) を選択して下さい。

pic_6

結果、下図が表示されますので、上部のタブから右端の「編集」を選択し、「自動構文チェック」のチェックボックスを外し、「変数の宣言を強制する」のチェックボックスをチェックして下さい。

pic_77

ソースプログラム入力先の「関数」と「ボタン」の作成

VBAでソースプログラムを入力し、実行する方法には、幾つかの方法がありますが、本講座ではExcelのシート上に専用のボタンを追加し、そのボタンが押されたときに入力したソースプログラムを実行する方法を紹介します。

Excelで新規に空のファイルを開いて、シート”Sheet1”を表示した状態で「開発」タブ(下図の⑧)を選択します。更に「挿入」(下図の⑨)をクリックします。すると下図のような「フォームコントロール」とその下に「ActiveXコントロール」が表示されますので、「ActiveXコントロール」の中の上段の右端の「コマンドボタン(ActiveXコントロール)」(下図の⑩)をクリックして下さい。

pic_8

カーソルが矢印から「+」に変わりますので、ドラッグして、適当な大きさのボタンを作成して下さい。結果、リボンの「デザインモード」が選択された状態になります。

pic_9

上図の状態で、作成したボタンをダブルクリックして下さい。Visual Basic Editorが起動し、以下の画面が表示されます。

pic_10

ここまで作業した時点で、新規作成したファイルを「名前を付けて保存」で保存して下さい。Visual Basic Editorでは無くExcelの画面に戻って、「ファイル」タブ⇒「名前を付けて保存」ボタンをクリックし、開いたダイアログで「ファイルの種類」として「Excelマクロ有効ブック(*.xlsm)」を選択して、適当なファイル名で保存して下さい。

pic_11

以上の操作により、プログラムを実行するためのボタンと、そのボタンが押された際に実行される「関数」を作成したことになります。

ソースプログラムの入力方法

再びVisual Basic Editorの画面に戻り、紙に書いて作成したソースプログラムを下記の空行の部分に入力して下さい。

src1

第3回の演習問題の解答例を入力すると、以下のようになります。

src

この画面は、左画面のシート名表示から「Sheet1(Sheet1)」が選択されている場合に表示されます。万一、別のファイルが選択され、上記の部分が表示されなくなった場合は、左画面から「Sheet1(Sheet1)」をダブルクリックして、表示し直して下さい。

プログラムの実行方法

入力したプログラムを実行する方法には、以下の2つの方法があります。

  • Visual Basic Editorの画面を使って、少しずつ実行する方法。
  • Visual Basic Editorの画面は使わずに、Excelの画面から一気に実行する方法。

前者は「プログラム作成途中」の実行方法で、プログラムに間違いが無いか動作確認しながら少しずつ実行する方法です。後者は「プログラム完成後」の実行方法です。

プログラムを少しずつ実行する目的について(=デバッグ)

入力したソースプログラムに間違いがあった場合、プログラムは期待通りに動作しません。この場合、入力したソースプログラムを読み直し、自分で間違っている箇所を探し出す必要があります。

しかし、ソースプログラムを読み直しただけでは、どこが間違っているのか解らない事が多々あります。この場合は、プログラムを1行ずつ実行して、変数に記憶した値の変化や、Excelのシート/セルの変化を確認しながら、ソースプログラムの間違った箇所を特定する必要があります。

このようにVisual Basic Editorを使ってプログラムを少しずつ実行させ、その時の動作を確認することでプログラムの間違った箇所を見つける作業のことを「デバッグ」と呼びます。

プログラムを一気に実行する方法

Excel画面に戻り、「開発」タブを選択して「デザインモード」をクリックして、デザインモードが選択されていない状態に戻してください。「デザインモード」が選択されていない状態では、「デザインモード」のボタンの色がオレンジ色から白に戻ります。

「デザインモード」のボタンが選択されていない状態になったら、シート上のボタン「CommandButton1」をクリックして下さい。結果、入力したプログラムが実行されます。

プログラムを途中で止める方法

プログラムの実行に先立って、途中で止めたい行を選びます。Visual Basic Editorの下記画面でプログラムの左端の部分(下図の⑪)をクリックすると、選択した行の色が変わります。この状態で「ブレークポイント」が設定された状態になります。その後、Excelの画面に戻って「CommandButton1」ボタンをクリックすると、プログラムが実行され、事前に選択した行で止まります。
もしくは、プログラムの実行中、今止めたい…と思った場合は、「中断」ボタン(下図の⑫)をクリックします。

pic_12

プログラムを一行ずつ実行する方法

プログラムが途中で止まっている状態で、ツールバー上の「ステップ イン(F8)」ボタンか、「ステップ オーバー(Shift+F8)」ボタンのいずれかをクリックします(下図の⑬)。結果、1行だけ実行され、次に実行する行を表す黄色のラインが移動します。
pic_13

上図⑬の拡大図を以下に示します (左が「ステップ イン(F8)」で右が「ステップ オーバー(Shift+F8)」)。

pic_14

今のところ、この2つのボタンの何れをクリックしても、同じ動きとなり、1行分、プログラムを実行した後に止まります。この2つのボタンは、ご自分で関数を作成された場合で、かつその関数呼び出しを実行する場合に限り、動作が異なります。「ステップイン」は、呼び出した関数の中に入ってから止まります。一方「ステップオーバー」は、呼び出した関数から戻るまで1回で実行します。

変数が記憶している値を確認する方法

プログラムが止まっている状態で、マウスカーソルを変数名の上に移動させると、「変数=値」という表示がポップアップで表示されます(下図の⑭)。

pic_15

他には「表示」⇒「ウォッチウィンドウ」メニューを選択してウォッチウィンドウを表示した後(画面の右下に表示されます)、「デバッグ」⇒「ウォッチ式の追加」メニューを選択し、表示されたダイアログの「式」の欄に確認したい式(変数名やオブジェクト変数名を含む)を入力する方法があります。結果、停止する度に入力した式の値が確認できます。下図は、式として「row」を入力した例です。

pic_16

プログラムを最初から実行し直す方法

リセットボタン(上図の⑮)をクリックした後、再度、Excelのシートからプログラムを実行する「CommandButton1」ボタンをクリックします。

デバッグの注意点

  • Do~Loop等の繰り返しを含むプログラムをデバッグする場合は、繰り返しの先頭か最後に必ず「DoEvents」文を挿入して下さい。
    作成したプログラムに誤りがあり、無限ループに陥る事は多々あります。この場合、繰り返しの中に「DoEvents」文を挿入しておかないと、中断ボタンやリセットボタンでプログラムを止める事ができなくなります。この事を考えると、Do~Loopを書く場合は、予めループ内の最後にDoEventsを書く習慣を付けられるのが良いと思います。
  • プログラムを再実行する場合は、開発ツールのリセットボタンをクリックしてからExcel画面のプログラム実行開始ボタンをクリックして下さい。

VBAによるプログラミング超入門講座(第3回)

このページでは、以下の内容について説明しています。

文字列について

プログラムで「言葉」の情報を扱うときは、データ型として「整数型」で無く、「文字列型」を使用します。文字列型の場合も、整数型のように定数と変数が使えます。

  文字列型の定数:  "abc" のように複数の文字を並べてダブルクォーテーション(")で囲んで表します。
  文字列型の変数:  「Dim 変数名 As String」として宣言し、以後、整数型の変数のように扱います。

文字列を扱うプログラム例

文字列を扱うプログラム例を以下に示します。If文で文字列の比較を2回行い、その結果を整数型の変数cmp1とcmp2にそれぞれ1(等しい)と0(異なる)で記憶し、最後に文字列型変数の代入を実行しています。このプログラムを実行した後、変数str1、str2、cmp1、cmp2にどんな値が記憶されるか、考えながら読んでいきましょう。

Dim str1 As String

Dim str2 As String

Dim cmp1 As Integer

Dim cmp2 As Integer

str1 = "abc"

str2 = "efg"

cmp1 = 0

cmp2 = 0

If str1 = str2 Then

cmp1 = 1

End If

If str1 = "abc" Then

cmp2 = 1

End If

str1 = str2

上記のプログラムでは、変数str1に “abc” という文字列を記憶させ、変数str2に “efg” という文字列を記憶させています。その後、If文で変数str1とstr2が等しいか比較しています。この場合、変数str1には”abc”、変数str2には”efg”という異なる文字列が記憶されているので、等しくなく、次の行の「cmp1 = 1」は実行されません。

続いて、変数str1と文字列定数”abc”を比較しています。この場合、変数str1には”abc”が記憶されているので、等しいと判定され、次の行の「cmp2 = 1」が実行されます。

続いて、変数str2で記憶していた文字列を変数str1に上書きしています。このプログラムの実行後、整数型の変数cmp1には0、cmp2には1が記憶され、文字列型の変数str1、str2には共に”efg”が記憶されます。

文字列型の最大文字数は

String型の変数には、何万文字も記憶できます。

文字列型の最小文字数は

String型の最小文字数はゼロです。文字数ゼロの文字列は””として表現します。Excelでは、空のセルを探すとき、空の文字列””を使用しますので、ここで文字数ゼロの文字列の使い方を覚えておいてください。

  str1 = ""
  If str1 = "" Then

文字列型の変数の中身は

コンピューターは、画像、音声、言葉等、全ての情報を「数」に変換して記憶しますので、「str1 = “abc”」と書けば、変数str1には文字a,b,cに対応する何らかの「数」が記憶されるはずです。また記憶した文字数もどこかに数で記憶されるはずです。

ですが、こういう細かい処理はVBAが勝手に処理してくれますので、我々は意識しなくて済みます。変数str1には任意の文字列が記憶でき、後でその情報が取り出せ、比較できれば充分…と割り切って、この疑問は先送りにしましょう。

関数について

プログラミング言語の翻訳ソフトには、役立つプログラム集が付属しており、我々が作るプログラムから利用できます。その個々のプログラムは「関数」と呼ばれます。VBAで利用できる関数には、たとえば画面にメッセージを表示する関数(MsgBox)、文字列を大文字に変換する関数(UCase)、文字列の中から別の文字列を検索する関数(InStr)等があります。

関数は単独で使える「作業指示書」

関数を利用するときは、コンピューターに対して「次の作業指示は、xxxxという名前の関数の指示に従ってくれ。その作業が終わったら再び私が書いた作業指示書のここに戻ってきて、次の行以降の作業指示に従ってくれ」というように、一時的に別の作業指示書で作業を指示する事になります。

関数は、「入力」を受け取り「処理」した結果を「出力」するような小さなプログラムです。我々のプログラムから関数を利用する場合は、以下のように書きます。

str1 = "abc"

str1 = UCase(str1)

If UCase("efg") = "ABC" Then

上記の例は、VBAが用意してくれている「文字列を大文字に変換する関数」である「UCase」を利用した例です。関数UCaseは、カッコ()の中に書いた文字列を受け取って、大文字に変換した結果を返します。関数が返した情報は、’=’を使って変数に記憶させるか、記憶する必要が無い場合、たとえばIf文の中で比較するだけで良い場合は、記憶させずにその場で参照するだけにします。

「UCase(str1)」や「UCase(“efg”)」の記述は、これまで式の中に変数や定数を書いてきた書き方に似ています。この記述は、カッコ内に書いた文字列が大文字に変換された文字列を指す事になりますが、コンピューターの動きとしては、一旦、ここに書かれているプログラムから離れて、別のところに書かれている関数UCaseのプログラムを実行し、その後、再びここのプログラムに戻ってくる…という動きになります。その際、関数UCaseを実行した結果として大文字に変換した文字列を持って帰ってくる事になります。

関数は「探して」使おう

VBAがサポートする関数の数は、膨大な数に及びますので、全部覚える事は難しいと思います。ですので、最初は「何がしたいか?」という我々の要求をはっきりさせてから、インターネットやVBAの入門書の目次/索引で検索する事をお勧めします。実は私も、VBAの関数はあまり覚えておらず、上記の関数UCaseもGoogleで「VBA 文字列 大文字 変換」というキーワードで検索して見つけました。

関数の仕様、たとえば

  • その関数はどういう仕事をするための関数か
  • カッコの中にどんな情報を幾つ書けば良いか
  • 渡す情報のデータ型は何型か?(整数型か文字列型か他の型か)
  • 関数が返す情報はどんな情報か、またその型は何型か

などの仕様は、関数によって異なりますので、これらについてもインターネットや書籍で調べてください。

オブジェクトについて

ここでは引き続き、プログラミング言語の翻訳ソフトに付属している役立つプログラム集について話をします。前項では「関数」の話をしましたが、プログラミング言語はその他にも「オブジェクト」と呼ばれる仕組みを使って、役立つプログラムを提供してくれます。

オブジェクトとは

オブジェクトは、複数の変数と、それに係る関数が一体化したものです。

オブジェクトの特徴

オブジェクトの特徴を以下に示します。

  • (変数のように)情報が記憶できます。
    オブジェクトは、自身に係る情報を記憶するための変数を「所有」します。
  • (関数のように)処理や仕事が実行できます。
  • (変数のように)名前を持ちます。

Excel表のセルを扱うオブジェクト「Range」

以下ではExcel VBAが提供するオブジェクト「Range」を例に挙げて、オブジェクトの特徴について更に説明していきます。RangeはExcelの「セル」を扱うオブジェクトです。セルの特徴を記憶する変数としてValue(値)、Row(行番号)、Column(列番号)、Width(セルの幅)、Height(セルの高さ)などの変数を所有しています。これらの変数に整数や文字列等を記憶させるときは、通常の変数と同じく‘=’演算子を使います。左辺にはオブジェクト名と変数名をカンマ(.)で繋げた名前を記述し、右辺には整数、文字列等を記述します。

オブジェクトRangeを使ったプログラム例

以下に、オブジェクトRangeを使ったソースプログラム例を示します。

Dim abc As Range

Set abc = Workbooks("成績表.xlsx").Worksheets("Sheet1").Cells(3,1)

abc.Value = 12345

abc.Font.ColorIndex = 5

MsgBox(abc.Value)

MsgBox(abc.Font.ColorIndex)

オブジェクト変数の宣言

オブジェクトを宣言するときは、変数のように「Dim オブジェクト変数名 As オブジェクト型名」と書きます。上記ではRange型のオブジェクト変数abcを宣言しています。

次の「Set abc = Workbooks(“成績表.xlsx”).Worksheets(“Sheet1”).Cells(3,1)」は、宣言したオブジェクト変数abcを使って、現在開いている”成績表.xlsx”のシート”Sheet1”の「3行目の1列目」のセルを扱うための記述です。この記述については後程、説明しますので、ここでは以後、オブジェクト変数abcを使って、”成績表.xlsx”のシート”Sheet1”の「3行目の1列目」のセルが扱えるようになる…とだけ理解しておいてください。

セルの中身を表す変数

「abc.Value = 12345」は、セルの値を保持する変数Valueに整数を記憶させる記述です。記憶した情報は後で取り出せます。たとえば「MsgBox(abc.Value)」と書けば関数MsgBoxに先ほど記憶した整数12345が渡せますし、整数型の変数iに取り出すときは「i = abc.Value」と書けます。

ここで重要な事は、整数型の変数なら単に情報を記憶するだけですが、オブジェクトの場合は記憶すると同時に、記憶した情報に関連する何らかの処理も併せて実行できる事です。Range型オブジェクトが所有する変数Valueに値を記憶させた場合は、セルの中身が記憶した値に変わり、Excelの表に反映されます。Excelの画面でもその変化が確認できます。

フォント色を表す変数

「abc.Font.ColorIndex = 5」は、フォントの情報を持つ変数Fontに色情報を記憶させています。ここで注目して頂きたいのは、変数Fontもオブジェクト変数であり、フォントの色情報を保持する変数ColorIndexを持っているという点です。この場合は前記のように、オブジェクト変数名をピリオド(.)で区切って並べて書きます。値5は、青を示す番号なので「abc.Font.ColorIndex = 5」を実行した後は、「3行目の1列目」のセルのフォントは青に変わります。

ブック、シート、セルを表すオブジェクトについて

Excel VBAでは、そのときに既に開いているExcelのファイル(Excelではファイルの事をブックと呼びます)や、その中のシート、更にその中のセルに相当するオブジェクトは、プログラム開始の時点で既に存在しています。これらのオブジェクトの間には「所有」の関係があり、ブックはシートを所有し、シートはセルを所有しています。ですので、たとえば現在開いているブック” 成績表.xlsx”のシート”Sheet1”の中に存在する「3行目の1列目」のセルに整数1234を入れる場合は、以下のように書きます。

 Workbooks("成績表.xlsx").Worksheets("Sheet1").Cells(3, 1).Value = 1234

上記のオブジェクト変数「Workbooks」、「Worksheets」、「Cells」は、それぞれブック、シート、セルの情報を扱うオブジェクトですが、いずれも複数のオブジェクトから成るオブジェクトです。したがって複数のオブジェクトの中から所望のオブジェクトを特定するために、カッコを付けてブック名、シート名、行列番号を指定する必要があります。

上記の例では、カッコの中は全て文字列および整数の「定数」で指定しましたが、変数で指定する事も可能です。

Dim fname As String

Dim sname As String

Dim i As Integer

Dim j As Integer

fname = "成績表.xlsx"

sname = "Sheet1"

i = 3

j = 1

Workbooks(fname).Worksheets(sname).Cells(i, j).Value = 1234

プログラムの中でこれらの変数の値を書き換えれば、同じ式で異なるファイル、シート、セルが扱えるようになります。

オブジェクトの「参照」について

前項では、Excelのセルを扱う際、以下のような長い文を書きました。

  Workbooks("成績表.xlsx").Worksheets("Sheet1").Cells(3, 1).Value = 1234

しかしセルを操作する度に、毎度このような長い文を書くのは大変です。よってVBAや他のプログラミング言語では、オブジェクト変数を使って別のオブジェクトを「参照」できるようになっています。「オブジェクトRangeを使ったプログラム例」で説明した下記プログラムは、Range型のオブジェクト変数を使って別のセルを参照している例です。

  Dim abc As Range
  Set abc = Workbooks("成績表.xlsx").Worksheets("Sheet1").Cells(3,1)

以下では、シートを扱うWorksheet型オブジェクトを使って、ブック”成績表.xlsx”の中のシート”Sheet1″を参照するプログラム例を紹介します。

  Dim table As Worksheet
  Set table = Workbooks("成績表.xlsx").Worksheets("Sheet1")
  table.Cells(3,3).Value = “xyz”

上記の例では、Worksheet型のオブジェクト変数tableに対して「Set オブジェクト変数名 = オブジェクト変数名」という文を使って、ブック”成績表.xlsx”の中のシート”Sheet1″の「参照」を記憶させています。以後、このオブジェクト変数tableを使えば、ブック名”成績表.xlsx”とシート名”Sheet1″は書かずに、同じシートが参照できるようになります。

「値」の記憶と「参照」の記憶の違いについて

前項で説明したオブジェクト変数の「参照」を記憶する…という意味について、もう少し説明します。これまで整数型や文字列型の変数に対して、’=’ の演算子を使って「変数名=別の変数名」と書いた場合は、右辺の変数が記憶していた「値」がコピーされるだけでした。この事を改めてプログラム例で見てみましょう。

Dim i1 As Integer

Dim i2 As Integer

i1 = 10

i2 = i1

i1 = 20

MsgBox i2    '10が表示される

前記のプログラムでは、最初に整数型の変数i1に10を記憶させた後、「i2 = i1」という文を使って、変数i1が記憶していた値を変数i2に記憶させています。その後、変数i1を「i1 = 20」という文で書き換えていますが、この記述は変数i2が記憶している値には影響しません。故に次の「MsgBox i2」を実行した場合、10が表示されます。

これに対して、オブジェクト変数に対して「Set」と’=’ の演算子を使って「Set オブジェクト変数名 = オブジェクト変数名」と書いた場合は、先程の動きとは違ってきます。

Dim t1 As Worksheet

Set t1 = Workbooks("成績表.xlsx").Worksheets("Sheet1")

t1.Cells(3,3).Value = “xyz”

Workbooks("成績表.xlsx").Worksheets("Sheet1").Cells(3,3).Value = “abc”

MsgBox t1.Cells(3,3).Value   ' abc が表示される

「Set t1 = Workbooks(“成績表.xlsx”).Worksheets(“Sheet1”)」の文によって、オブジェクト変数t1に対して、「ブック”成績表.xlsx”の中のシート”Sheet1″」の「参照」が記憶されます。これは右辺の記憶していた「値」がコピーされるのでは無く、右辺の存在自体が記憶される…という意味になります。よって以後、左辺のオブジェクト変数と右辺のオブジェクト変数は、同じ実体「ブック”成績表.xlsx”の中のシート”Sheet1″」を指すようになります。

演習問題(その2)

では、オブジェクトを使った演習問題をやってみましょう。Excelで作成した以下のような”成績表.xlsx”が既に開かれているとします。

成績表

この既に開かれている”成績表.xlsx”のシート”Sheet1”を操作して、60点以上の生徒の「名前」と「点数」を青色に変えるプログラムを作成してください。

ヒント

  • セルのフォントの色を青色に変える方法は「フォント色を表す変数」のソースプログラム例を参考にしてください。
  • シート”Sheet1″の2列(B列)目に着目して、2行目から最後の行まで、60点以上かどうか繰り返し確認してください。
  • 表の終わりは、セルが空かどうかで判定してください。
    空のセルでは、セルが所有する変数Valueが空の文字列(ダブルクォーテーション2つ “”で表す)になります。
  • If~Else~End If の中で「Else~End If」間の文が不要なときは、省略して「If~End If」だけで書けます。

解答例

以下、解答例です。

Dim table As Worksheet  ' シートの参照を記憶するオブジェクト変数

Dim row As Integer    ' シートの行番号を記憶する変数

Set table = Workbooks("成績表.xlsx").Worksheets("Sheet1")

row = 2                   ' 2行目から調べる

Do

If table.Cells(row, 2).Value = "" Then  ' 最後まで調べ終わったら終了

Exit Do

End If

If table.Cells(row, 2).Value >= 60 Then

table.Cells(row, 1).Font.ColorIndex = 5

table.Cells(row, 2).Font.ColorIndex = 5

End If

row = row + 1

Loop

VBAによるプログラミング超入門講座(第2回)

このページでは、以下の内容について説明しています。

プログラム例の解説

では、第一回の「プログラミングの基本作法(マナー)は」を思い出しながら、「(ある処理) を3回、繰り返す」という超簡単なプログラム例を再び見て行きましょう。

Dim i As Integer

i = 1

Do

If i > 3 Then

Exit Do

Else

(ある処理)

i = i + 1

End If

Loop

Dim i As Integer

先頭の「Dim i As Integer」は『今から「i」という名前の「変数」を使います。その「型」は「整数型」です』…という事を「宣言」する文です。「Dim」と「As」の部分は、VBAでは「こう書いて下さい!」と厳密に決められている部分です。「語源は何だろう?」とか考えずに、この通りに書いてください。

「i」の部分は変数名です。ここで、使用する変数の名前を宣言します。変数名には、好きな名前を使う事ができます。変数の名前に使用できる文字の種類は、プログラミング言語によって異なりますが、先頭に「英字」、2文字目以降に「英字」か「数字」を使っておけば、殆どのプログラミング言語で変数として認識されるはずです。

「Integer」は「整数」という意味です。ここで変数の「型」を宣言します。変数の「型」とは、記憶する情報の「種類」の事です。「Integer(=整数)」と宣言する事で、以後、この変数iを使って整数が記憶できます。整数は、1で割り切れる正または負(マイナス)の数で、…,-3, -2, -1, 0, 1, 2, 3,…などの事です。VBAでは、整数型の変数を使って -32768 ~ +32767 の間の「いずれかの整数値」が記憶できます。変数の「型」には他にも幾つか種類がありますが、この講座では最初は整数型だけを使って行きます。

「=」は、変数に値を記憶させるときに使う記号(演算子と言います)です。小学校で習った算数では「=」はたとえば「1+2=3」と書いて、左の式と右の式は「等しい」事を表しますが、プログラミング言語では「=」は、「左の変数に右の式を記憶させる」事を表します。算数に慣れ親しんだ我々にとっては解り辛いですが、そういうルールなのだと、ここで覚えておきましょう。

ちなみに変数に情報を記憶させた後、後の文で再び「=」を使うことで、別の情報を記憶させる (上書きする)事ができます。

Do~Loop

次の「Do」と、その7行先にある「Loop」はペアになっています。「Do」文と「Loop」文を書くと、間で挟んだ文を繰り返し実行してもらえます。

Do

(処理1)

Loop

たとえば、上記のように書けば、(処理1)を実行した後、再びDo文に戻って(処理1)を実行し、これを無限に繰り返してもらえます。ただし「ある条件になったら繰り返しを止めて欲しい」場合は「Exit Do」を実行してもらえれば、そこで繰り返しのループから抜けて、Loop文の次の行から実行してもらえます。

If~Else~End If

If i > 3 Then

Exit Do

Else

(ある処理)

i = i + 1

End If

状況/条件に応じて「もし~なら~を実行し、そうで無いなら~を実行する」というように、条件に応じて実行する処理を変えて欲しいときは「If~Else~End If文」を使います。

「If i > 3 Then」は「もし変数iが3より大きいなら」という意味です。これが正いなら、コンピューターはIfとElseで囲んだ文を実行し、違うならElseとEnd Ifで囲んだ文を実行します。これにより「Exit Do」文は変数iが4になったときに実行されます。

If の後には以下のような「条件式」を記述します。

左辺 = 右辺
左辺と右辺が等しいとき
左辺 <> 右辺
左辺と右辺が等しくない(異なる)とき
左辺 > 右辺
左辺が右辺より大きいとき
左辺 < 右辺
左辺が右辺より小さいとき
左辺 >= 右辺
左辺が右辺以上(等しいか、より大きい)のとき)
左辺 <= 右辺
左辺が右辺以下(等しいか、より小さい)のとき

ここでまた’=’が出てきました。VBAではIf文で「If i = 3 Then」と書いた場合は、変数iで整数3を記憶するのでは無く「もし変数iが 3と等しいなら」という意味に変わります。同じ’=’という演算子でも、書く場所によって意味が変わるのです。ややこしいですが、VBAで決まっているルールですので、ここで覚えておきましょう。

左辺や右辺には、式を記述します。式には、変数、定数(決まった数のことです)、演算子が記述できます。演算子については、後で説明します。

Else~End If

Else

(ある処理)

i = i + 1

End If

「Else~End If」の間には、(ある処理) と 「i = i + 1」という2つの文が書かれています。(ある処理) は、ここでは仮に日本語で(ある処理) と曖昧に記述していますが、実際にはここで実行して欲しい計算式を記述する事になります。繰り返し実行して欲しい処理をここに記述するのです。

その次の「i = i + 1」は、現在のループ回数を記憶するための文です。iに1を足した値が再度、iに記憶されます。

演算子について

ここで「式」に使用する演算子について説明します。演算子にはいろんな種類がありますが、この講座では四則演算(足し算‘+’、引き算‘-’、掛け算‘*’、割り算‘¥’)に加えて、割り算の余りを求める Modを紹介します。たとえば、整数型の変数iとjがあり、jに13が記憶されていたとします。

このとき「i = j ¥ 3」は、変数jを3で割った結果(商)を記憶するときの指示です。結果は整数型で、余りは切り捨てられます。この例では変数iに商の4が記憶されます。

また「i = j Mod 3」は、変数jを3で割った余り(剰余)を記憶するときの指示です。結果は1になり、iには1が記憶されます。

ちなみに割り算には演算子‘/’ も使えますが、割り切れない場合は商が偶数になるよう、小数点以下の切り上げ、または切り捨てが発生してしまいます。

プログラム例の動き

以上の説明を踏まえ、このプログラムがどういう動きをするか、もう一度、見て行きましょう。説明し易いように、各行頭に行番号を記載しました

1:       Dim i As Integer

2:

3:       i = 1

4:       Do

5:           If i > 3 Then

6:              Exit Do

7:           Else

8:              (ある処理)

9:              i = i + 1

10:          End If

11:      Loop

12:

  • コンピューターはまず1行目を実行します。ここでは整数を記憶するための変数i を宣言しています。これにより以後、変数i が使用できます。
  • 2行目を実行します。空行なので何もしません。
  • 3行目を実行します。変数i を使って、最初の値(初期値といいます)として1を記憶します。
  • 4行目を実行します。「Do」文ですので、Loop文までの文を繰り返し実行せよ…という指示になります。
  • 5行目を実行します。If文ですので、ここに記載された条件式により、その次に実行する行が変わります。
    このときの変数iには1が記憶されているので、3より大きいという条件は満たしません。よって次に実行する行はElse文の次の8行目になります。
  • 8行目を実行します。ここでは仮に日本語で(ある処理)とだけ記載していますが、ここに実際の計算式を書けば、その式が実行されます。
  • 9行目を実行します。ここでは変数iに 1を足した値が変数iに記憶されます。結果、変数iの値は 1から2に変わります。
  • 10行目を実行します。End Ifですので、ここでIf~Else~End Ifの条件判定が終わり、次は11行目を実行することになります。
  • 11行目を実行します。Loop文ですので、次に実行する行は対応するDo文が書かれている4行目になります。「Do」文ですので、Loop文までの文を繰り返し実行せよ…という指示になります。

・・・というように、一連の処理を繰り返し実行する事になります。このDo~Loopの繰り返しの中で、変数i に記憶した値がどう変化するか見て行きましょう。If文のある5行目の位置で見てみます。
   1回目:1
   2回目:2
   3回目:3
   4回目:4で「If i > 3 Then」が正しくなり、次の「Exit Do」文が実行されます。

結果、Do~Loopの繰り返しから抜けて、次は12行目を実行する事になります。
このプログラム例では、変数i を使って値1から3まで数える事で、同じ処理を3回繰り返しましたが、1から10まで数えて10より大きい11でExit Doを実行しても構いません。

演習問題(その1)

では早速、実際にソースプログラムを書いてみましょう。ここで書くのは、以下のようなプログラムです。

  • 1+2+3+4+5+…+9+10 というように、1から始めて1大きい値を10まで数えて、全部を足し算していくプログラムです。
  • 計算結果は、整数型の変数ans を宣言して、そこに記憶して下さい。

※変数は「Dim 変数名 As Integer」という文を使って、何個でも宣言できます。

ポイント

  • このプログラムでも「繰り返し」「条件判定」を使って下さい。
    本ページの冒頭でご紹介したプログラム例の中で仮に日本語で(ある処理)と書いたところは、変数と定数を使った「実際の式」に書き換えてください。
  • どういう計算を何回、繰り返さないといけないか、考えてください。
    実際に自分で計算してみて、どんな計算を繰り返す必要があるか、計算の規則を発見してください。
  • 人間と同様、コンピューターも前回の計算結果を「記憶」しておかないと、次回の計算ができません。この点に注意してください。
  • 解答例

    Dim i As Integer

    Dim ans As Integer

    i = 1

    ans = 0

    Do

    If i > 10 Then

    Exit Do

    Else

    ans = ans + i

    i = i + 1

    End If

    Loop

VBAによるプログラミング超入門講座(第1回)

このページでは、以下の内容について説明しています。

コンピューターとプログラムの関係について

パソコンやスマホは我々にとって身近なコンピューターですが、パソコンやスマホの製造メーカは、BIOS、OS、アプリなどのプログラム(ソフトウェアとも言います)をパソコンやスマホに組み込んだ状態で販売しています。もしこれらのプログラムが無かった場合、コンピューターはどうなるでしょうか?…画面には何も表示されず、キーボードやマウスからの入力にも反応しない「ただの箱」になってしまいます。

コンピューターは、プログラムがあって初めて動作する機器なのです。

プログラムとは

プログラムは、コンピューターに対する「作業指示書」の役目を果たします。

コンピューターを人間にたとえると…(性格診断)

コンピューターは、人間にたとえると「実直」で「真面目」ですが、「自主性」に欠けています。作業指示書で指示された事は確実に実行してくれますが、作業指示書が無いと何もしてくれません。じっと動かず止まったままです。また融通が利きません。人間の得意技である「適当に」「思い付き」で判断する事もできません。

よってコンピューターへの作業指示書であるプログラムは、起こり得る全ての状況/条件を想定して、厳密にコンピューターに作業を指示する「文書」でなければなりません。

プログラムは「0」と「1」で出来ている

実は、コンピューターは「数」しか理解できません。よってプログラムは数で書かれています。0か1が大量に並びます。0か1が何個か集まると大きな数が表現できます(2進数表記といいます)ので、その個々の数に意味を持たせて、数によって仕事を指示するようにしています。

コンピューターにこの数が書かれたプログラムを読んでもらうには、コンピューター内の「メモリ」に転送して、プログラムをメモリから読み込んでもらう必要があります。

プログラミングとは

プログラミングとは、プログラムを書く作業/行為のことです。

前節では、プログラムは「0」と「1」で出来ている…と説明しましたが、人間にとって、数だけで作業指示書を書く事は大変難しい作業になります。よって「数」より解り易い「専用の言葉(=言語)」を使って「作業指示書」を書くのが一般的になっています。この特別な言葉の事を「プログラミング言語」といいます。

この考え方は、たとえば日本人がアメリカ人に作業を依頼する際、「日本語」で作業指示書を書いて、翻訳ソフトで「日本語」から「英語」に翻訳して、アメリカ人には「英語」で仕事を依頼する…という考え方と同じです。

プログラミングでは、人間が理解できる「プログラミング言語」からコンピューターが理解できる「機械語」に翻訳するための「翻訳ソフト」(コンパイラやインタープリタといいます)を使う事になります。

Microsoft社のVBA(Visual Basic for Applications)とは

VBAは、Microsoft社のOffice製品(Excel、Access、Word等)がサポートするプログラミング言語です。

人間が使う言語にも種類があるように(例:日本語、英語、フランス語等)、プログラミング言語にも幾つか種類があります(例:C, C++, C#, Java, JavaScript, Ruby, VBA等)。VBAはOffice製品の文書編集を「自動化」するのに適したプログラミング言語です。Office製品には、VBAで書いた作業指示書を数字で書いたプログラムに翻訳するソフトが内蔵されています。

本講座ではVBAを使ってプログラミングを勉強します。

コンピューターの基本動作

普段、我々がゲーム、ワープロ等を使用するとき、コンピューターは非常に複雑な仕事をしてくれていますが、このような複雑な仕事は、実は単純な基本動作の繰り返しで実現されています。

コンピューターの基本動作は「情報を入力して、計算/比較して、結果を出力する」動作です。

たとえばWindowsに付属する「電卓アプリ」では、キーボードから計算式の情報を入力して、計算して、結果を画面に出力しています。これがコンピューターの典型的な基本動作なのです。

プログラミングでは、このように「情報を入力して、計算/比較して、結果を出力する」…というような基本動作を繰り返し作業指示書に書き、コンピューターに作業を依頼します。複雑な仕事を一気に実行してもらうのでは無く、簡単な仕事を繰り返し依頼する事で、結果として複雑な仕事を実行してもらうのです。

プログラムの例

百聞は一見に如かず、ここでは超簡単なプログラムの例を見てもらいます。VBAというプログラミング言語を使って「(ある処理) を3回、繰り返す」だけの作業指示を書いた例です。繰り返したい処理は、今の段階では決まっていないので、ここでは仮に日本語で(ある処理)と書いておきます。

なおプログラミング言語で書いた作業指示書のことを「ソースプログラム」といいます。

Dim i As Integer

i = 1

Do

If i > 3 Then

Exit Do

Else

(ある処理)

i = i + 1

End If

Loop

プログラミングの基本作法(マナー)は

1行で1つの仕事を依頼する

同時に沢山の仕事を依頼せずに、できるだけ1つずつ仕事を依頼するようにします。コンピューターは、プログラムの一番上から順に1行ずつ読み込んで、1行ずつ実行してくれます。この基本ルールを考えて、ソースプログラムを書いてください。

繰り返しを活用する

幾つかの行を繰り返して実行して欲しいときは「Do~Loop」文を使います。コンピューターは「Do」文と「Loop」文で挟んだ間の文を繰り返し実行してくれます。その繰り返しの中で「Exit Do」文が実行されると、繰り返しを終えてLoop文の次の行から実行してくれます。

この「繰り返し」の書き方は、プログラミング言語によって異なりますが、概ね似ています。

条件判定を活用する

その時の状況/条件に応じて「もし~なら~を実行し、そうで無いなら~を実行する」というように、実行して欲しい処理を変えたいときは「If~Else~End If文」を使います。

「そうで無いなら~を実行する」が不要な場合は「Else~」以降は省略できます。

また~の部分に更に階層的に入れ子で「If~Else~End If文」を書く事もできます。

この「条件判定」の書き方も、プログラミング言語によって異なりますが、概ね似ています。

変数を活用する

外部から入力した情報、計算の途中結果などの情報は「変数」を使って小まめに保存(記憶)します。

変数を利用する目的

「コンピューターの基本動作」で説明したとおり、コンピューターの基本動作は「情報を入力して、計算/比較して、結果を出力する」ですが、このときの「入力した情報」、「計算した結果」、「出力したい情報」など、全ての情報は「変数」を使って小まめに記憶させる必要があります。そうしないと、情報が消えてしまって、再び参照できなくなるのです。

よって後で再び参照したい情報は、変数を使って小まめに記憶させましょう。

変数を使って情報を記憶する作法は、たとえば家の中で書類整理をするときに、適当な「箱」に好きな名前を書いて、その中に書類を入れる事で、再び書類を取り出しやすくする…という整理作法に似ています。

変数には、好きな名前を付ける事ができます。

夢のマンガ作成ソフト「コミPo!」

ブログを始めるにあたり、文書と写真に加えて、絵を載せたいと思っていました。それでAmazonで適当なグラフィックソフトが無いか、探したところ「コミPo!」なる素晴らしいマンガ作成ソフトを見つけました。

かつて夢見た理想が現実に

大ヒットアニメ「アナと雪の女王」は、3Dアニメーションです。人物の輪郭、骨格の位置情報を三次元でモデル化して、その情報をコンピューターで再計算することで、人物の動いたときの映像を作り出し、2D画像としてスクリーン上にコマ送りで表示しているのだと思います。

現実の世界で言うと、関節が自由に動く人形の関節や向きを少しずつ変えて写真を撮り直し、コマ送りしているような手法だと思います。

この3Dアニメーションの手法は、「マンガ」の作成にも応用できるので、いずれその種の安価なグラフィックソフトが開発されて、我々の表現手段として使えるようになるのでは? と期待していました。今回ご紹介する「コミPo!」は、まさにそういう夢のグラフィックソフトです。

「コミPo!」を使ったマンガの作り方

何人かの人物のの3Dモデルが予め用意されています。それを上下左右に回転させる事で、色んな角度から見た人物の絵を簡単に作る事ができます。また関節の動きについては、何通りかパターン(たとえばバンザイする、指を立てる等)が予め用意されているので、その用意されたパターンを使います。

モデルの表情、髪、服、小物(めがね等)も沢山用意されており、着せ替え人形のように自由に取り換えて使う事ができます。背景や、マンガ特有のセリフの部分(雲のような形)も幾つかの種類が用意されています。

まずは無料体験版からスタート

最初に「コミPo!」の公式サイト から無料体験版をダウンロードして使ってみる事にしました。この種のソフトを最初に使うとき、私はマニュアルを読まずにどこまで使えるか試します。結果、この「コミPo!」はマニュアルを読まなくても、30~1時間程、試してみるだけで 充分に使える事が解りました。

ただ私は、過去にMicrosoft社のWord/Excel/PowerPoint等で「絵」を書いた経験がありますので、そういう経験の無い方は、もう少し時間がかかるかも知れません。

入門者向けのチュートリアルやユーザーズマニュアルは「コミPo!」公式サイト内のページ からダウンロードできますので、以下では細かい話はせずに、私が試した手順だけをご参考までに記載します。

  • 起動すると、どういうコマ数のマンガを作成するか、聞いて来ます。
    体験版では、4コママンガ以外は作成できなくなっていますので、初期画面が出てくるまでは、全て「OK」ボタンを押します。
  • すると、以下のような初期画面が出て来ます。

初期画面

  • 最初にやってみる事は、画面左端に縦に並んでいるアイコンを上から順にクリックしてみる事です。そうすると1つ右の列に絵の「部品」が出てきます。それを見れば、先ほど選択したアイコンが、何の「部品」のためのアイコンであったか確認できます。

たとえば

左端の一番上から3つ目のアイコンは、登場人物を選択するアイコン、
5番目のアイコンはセリフを書く雲の形を選択するアイコン、
6,7,10番目のアイコンは背景を選択するアイコン、
8,9番目のアイコンはマンガ特有の効果(ギザギザの模様、「バシッ!」のような書き文字) を選択するアイコン…

という事が解ります。

  • 下の図は、左端の上から3つ目のアイコンを選択し、その右の列に登場人物が表示されたときの画面です。

登場人物

  • 前記の操作で1つ右の列に「部品」が表示されたら、それを「ドラッグ&ドロップ」の操作で、画面中央の何れかのコマの部分にドロップします。すると選択した部品が指定したコマの中に表示されます。
  • 上の操作で置いた部品が選択された状態(1回クリックすると選択できます)では、周囲に破線、四隅に小さい四角、四辺の中央に小さい丸が表示されます。この状態で、破線、小さい四角、小さい丸をクリックしたままマウスを動かしてみて下さい。結果、拡大/縮小/回転等ができるはずです。このとき、登場人物の部品だけは、3Dの回転が可能なため、他とは違う小さな丸/四角が表示されますが、先ほどと同様に操作してみれば、どういう動きになるか確認できると思います。

コミPo!

  • 部品が選択された状態では、その属性を変更するためのアイコンが、画面上部、および右端に並びます。マウスを各アイコンの上に置く(暫く停止させる)と、そのアイコンの短い説明が表示されますので、どういう属性を変更するためのアイコンであるか確認できます。

たとえば登場人物なら、
表情、
ポーズ、
パーツ(メガネ等)
等を変更するアイコン等があります。

  • セリフは、雲の形の部品を置いて、それを選択した状態で右端の上の方のテキスト入力領域に入力します。
  • 部品は、背景、人物、セリフ等、重ねて描いていきますので、途中の階層の部品をマウスで選択する事が難しくなります。
    (たとえば背景を選択したつもりが、一番上の階層の部品を選択してしまう、等)。
    この場合は、画面右下の「レイアウトリスト」を使って、任意のコマの任意の階層の部品を選択できます。

以上、私なりのチュートリアルでした。この無料体験版で作成した4コママンガは以下です。

Comic_001

オチも笑いも無い….とお嘆きの方もおられるとは存じますが、あくまで「コミPo!」無料体験版で作成したサンプルですので、ご容赦ください。画像の保存は、ファイル⇒画像ファイル出力 メニュー選択で行えます。

なお無料体験版では、保存する画像の解像度が固定になっており、また作成途中のデータを一時保存する事もできません。登場人物等の部品の数も少ないため、実際に「コミPo!」を活用される方は、製品版を購入される事をお勧めします。

ちなみに私は、製品版「コミPo! ビジネス文書 マンガセット・スターターパック」 をAmazonで購入しました。