t-hom’s diary

主にVBAネタを扱っているブログ…とも言えなくなってきたこの頃。

VBA マクロが遅い・速いという議論は、要件ありきの話

VBAのコードについて、よく、この手法は遅いから使うなという話を聞く。高速化万歳!
またはその逆で、高速化のためにわかりやすさを犠牲にするなどナンセンスだ!という話も聞く。

この記事では前者を「$バンザイ」、後者を「$ナンセンス」と呼ぼう。

$バンザイと$ナンセンスはどちらも自分の考えが正しいと信じており、否定しようものなら青筋を立てて反論してくることだろう。
問題はそこにある。あまりにも感情移入しすぎている。

私が思うに、$バンザイと$ナンセンスはどちらも正しくない。個々の要件、シチュエーションが考慮されていないからだ。

$バンザイは、マクロが高々1分速くなったところでビジネス的には大して変わらないという観点が抜け落ちている。
実行に15分かかる処理が14分になったところで、気づきもしないだろうし、そのような高速化にあまり意味はないと思う。
また、月に1度しか発生しない作業なら、2分を1分に短縮したところで高々知れている。
$バンザイは、そのような高速化のためにコードをひどく分りにくくしてしまうことがある。これは全くもってナンセンスだ。

一方で$ナンセンスは、たった1分でも人間は待たされることを苦痛に感じ、ストレスを溜める生き物であるという観点が抜け落ちている。
$ナンセンスは、「業務上の影響」だけを意識するため、5分も10分も変わらないじゃないかと思うようになる。確かに月に数回ならね。
しかし毎日何回も繰り返し実行するマクロでは、5分と10分の差はとてつもなく大きい。
5分と6分で感じるストレスは大差ないかもしれない。しかし1分と1秒で感じるストレスは、1分のほうが遥かに大きい。

3秒と10秒の差も無視できない。一瞬と1秒の差も大きい。

打てば響くようにキビキビ動くマクロは使っていて気持ちいいものである。仕事のリズムが良くなり、集中力が増す。
$バンザイの主張にもうなずけるものがある。

このように、マクロが遅い・速いという議論は、ユーザーが求めるスビード感(つまり要件)ありきの話なのだ。だから、個々のコーディングテクニック、作法を指して、これは遅いから絶対ダメ、これは分りにくいから絶対ダメという主張は鵜呑みにしないほうが良い。


さて、私は最近「ハッカーと画家」という書籍を読んだ。

どの章も面白かったが、特に気に入ったのは第11章「百年の言語」だ。

その中でも格別に素晴らしいと感じた箇所を引用する。

インフラが整った現在では、長距離電話をかけている最中に時間を分刻みで気にするなんて些細なことのように思えてくる。資源があれば相手がどこにいようと電話をかけるというのはひとつの均一な行為だと考えるほうがエレガントだ。
良い無駄と悪い無駄があるってことだ。私は良い無駄のほうに関心がある。贅沢に使うことで、より単純なデザインが得られるような無駄だ。新しい、速いハードウェアのマシンサイクルを無駄に使えるというチャンスをどんなふうに利用できるだろう。

本当の非効率性とは、マシンの時間を無駄にすることではなく、プログラマの時間を無駄にすることだ。コンピュータが速くなればなるほど、このことははっきりしてくる。

※マシンの時間とは、おそらく我々の考える「時間」を指して言っているのではない。CPUやメモリの使用率の話だと思う。

私もこの意見に同意する。これは$ナンセンスが言う、5分も10分も変わらないという意見ではない。
時代の進歩とともに、それはやがて1分になり、30秒になり、一瞬になるだろうという話である。

$バンザイが目指す高速化至上主義が時代によって自動的に解決されるのであれば、これからのプログラミングはわかりやすさ、メンテナンスしやすさ、プログラマの効率にフォーカスを当てたものになるだろう。

だから、原則としてはより抽象化されたメンテナブルなコードをデザインすべきだと思う。
$ナンセンスはここでしたり顔で「ほらね」というかもしれない。

でもそれは違う。

現代においては依然としてそのような高速化は実現されておらず、たとえばExcel VBAではセルを個別に読みにいくような処理はすこぶる遅い。
そこで、配列転記のような高速化テクニックが必要になってくるわけだ。

でも最初から高速性ありきで考えるのではない。
まずはエレガント!!なグランドデザインがあり、求められるパフォーマンスが出ないときのチューニングとしての高速化テクニックを使用するのだ。

マクロの速度に関する議論は要件ありき。ただし、高速化テクニックはあくまでパフォーマンスチューニングであることに留意したい。コンピューターの、あるいはExcelの過渡期における必要悪である。決してそれがグランドデザインであってはいけない。

VBA 変数で躓いた方に贈る、くどいくらい丁寧な変数の説明

さて、今回は初心者向けの記事なので、本文を「ですます調」で書くことにする。
途中でこのように緑文字でコメントを入れる。コメントはいつもどおり「だ・である調」で書く。本文は黒文字とする。

プログラミングでは「変数(へんすう)」という道具を利用して一時的にデータを保存することができます。

数学でも変数という用語が出てきますが、いったんそれは忘れてください。数学では不定値を仮に「x」とするなどの使われ方をしますが、プログラミングで変数はデータの保存に使う道具なので、別物だと割り切って考えましょう。

プログラミングで変数に保存するのは「数」に限らず、「人の名前、物の名前、セリフ、日付、正しいか間違いかの判定結果」など様々です。

データを保存するには、「変数名 = データ」の形で記述します。ここでも、数学の「イコール」は一旦忘れてください。このイコールは「この変数にこのデータを保存してください」というコンピューターへの指示に使う記号です。

ではやってみましょう。
「こんにちは」という文字を「あいさつ」という変数に入れるには、このように書きます。

Sub サンプルマクロ()
     あいさつ  = "こんにちは"
End Sub

「え?」と思われた方もいるでしょう。変数って「x」とか「y」のことじゃないの?と。いえ、数学の変数とはもはや別物で、好きな名前をつけることができます。英数字しか使えないプログラミング言語が多いので一般的には「greeting」といった英語で命名することが多いですが、VBAでは漢字仮名も使えるので別に日本語の名前をつけても構いません。

変数は人が読んで意味の分かる名前を付けることが推奨されています。

さて、保存した値を利用するには、単に値の代わりに変数を書くだけです。保存と合わせてやってみましょう。

Sub サンプルマクロ()
    あいさつ = "こんにちは"
    MsgBox あいさつ
End Sub

これで「こんにちは」というメッセージを表示させることができました。でもこれだと何が嬉しいのかわかりませんよね。だって最初からこう書けばいいじゃないですか。

Sub サンプルマクロ()
    MsgBox "こんにちは"
End Sub

では次の例をみてみましょう。

Sub サンプルマクロ()
    yourName = InputBox("あなたの名前を入力してください。")
    MsgBox "こんにちは" & yourName  & "さん"
End Sub

このマクロは、ユーザーに名前を入力させてその結果を表示します。

以下のように変数を使わずに書くこともできますが、なんだか長ったらしくなって読みにくいですよね。

Sub サンプルマクロ()
    MsgBox "こんにちは" & InputBox("あなたの名前を入力してください。")  & "さん"
End Sub

それに、以下のように名前を2回表示させようと思ったら、やっぱり変数を使うしかありません。

Sub サンプルマクロ()
    yourName = InputBox("あなたの名前を入力してください。")
    MsgBox "こんにちは" & yourName  & "さん。"
    MsgBox yourName  & "さんはお元気ですか?"
End Sub

もし変数がなければ、名前を表示させるたびに尋ねないといけませんから。

このように変数は、「データを保存する道具」であると理解してください。そしてデータを保存することを、専門的には「代入」といいます。これも数学から用語だけ持ってきたような代物ですので、「代入」と聞いたら、「ああ、保存することだな」と思っていただければ結構です。

また、これからはデータのことを「値」と呼びます。数学用語ですが、プログラミングでは数値だけではなく、先ほどのように"こんにちは"という言葉も値です。

ですから、「変数にデータを保存する」ことを、一般的には「変数に値を代入する」といいます。数学チックな言い方なので嫌な思い出のある方もいるでしょうけど、書籍などではこの言い回しが一般的なので、慣れてください。所詮、数学とは別物です。

コメント
ここまで変数の本質的な説明を行ってきたので、いまさら箱だのメモだのといった説明は蛇足かもしれない。というか変数には形も色もないのだけれど、「具体的に変数とは何なんだろう」という余計な疑問を呼び起こし、混乱を増長させるような気がする。
ただ前回書いた「概念形成」の話を加味すると、実験的に一旦ここでは箱とかメモとかタグとか、色々な説明を混ぜておきたい。
ここまでの説明で十分納得された方は、「混乱を避けるために続きを読まない」という選択肢もあることにご留意いただければと思う。

変数はよく、箱のようだとか、メモのようだと説明されます。

たとえば「あいさつ」と書かれた箱に「こんにちは」という言葉が入っているところをイメージしてください。一度箱に保存すれば、いつでも中身を確かめることができます。また、中身を入れ替えることもできます。

こんな風に書くと、最初は「こんにちは」、つぎに「こんばんは」が表示されます。

Sub サンプルマクロ()
    あいさつ = "こんにちは"
    MsgBox あいさつ
    あいさつ = "こんばんは"
    MsgBox あいさつ
End Sub

最初に保存した「こんにちは」は上書きされて無くなってしまいます。この点、サイズの許すかぎり何個でも入る「箱」とは違いますね。

次に、「あいさつ」とタイトルが書かれたメモ用紙に「こんにちは」と書き込むところをイメージしてみましょう。実際のメモは余白が許す限り何個でも書けてしまいますが、実際の変数は上書きされますから、たとえ話はあくまでたとえ話だということを理解しておいてください。

ネームタグに例える説明もあります。たとえば、「あいさつ」と書かれたタグが、「こんにちは」という文に括り付けてあるイメージです。紐をほどいて、「こんばんは」に括りなおすこともできますね。このたとえはなかなか秀逸で、オブジェクト変数にもそのまま使えます。

しかし、いずれにしても所詮たとえ話なのでそこは割り切っておいてください。変数の本質は「データを保存する」という機能であり、何か具体的な色や形があるわけではないのです。

さて、変数のことがなんとなく理解できましたか?
正直いまひとつピンとこないという方も多いと思います。

ではもう少し具体的な話をしましょう。
具体的に、変数に保存したデータはどこにあるのでしょうか。
それは、コンピューターのメモリです。


コメント
またここで、実装に踏み込んだ説明をするべきかどうか悩むが、とりあえず書く。上記の説明でスッキリ理解できている人は読まなくても良いけど、逆にたとえ話で混乱が深まったという方には良い処方箋になるのではないかと思う。

メモリにはアドレス(番地)がついていて、たとえば4GBなら、0番地~21億4748万3647番地まであります。
このどこかに保存されているのです。10桁の数値で表すことができますね。

たとえばこんなプログラムがあったとします。

Sub サンプルマクロ()
    数値1 = 20
    数値2 = 10= 数値1 + 数値2= 数値1 - 数値2= 数値1 * 数値2= 数値1 / 数値2
    MsgBox 数値1 & " + " & 数値2 & " = " &MsgBox 数値1 & " - " & 数値2 & " = " &MsgBox 数値1 & " * " & 数値2 & " = " &MsgBox 数値1 & " / " & 数値2 & " = " &End Sub

するとコンピューターは変数名とその保存場所を示した表をメモリに作り出し、実際の値はメモリ上に保存されています。
メモリのどこに保存されるかはコンピューターが管理しているので、こちらでは指定できません。

たとえばこのあたりだとしましょう。

変数名 アドレス
数値1 2486820
数値2 2486788
2486756
2486740
2486724
2486708

VBAでは直接メモリのアドレスに値を入れたり取り出したりすることはできず、変数を介して間接的にアクセスするしかありませんが、仮に変数が存在せずアドレスを指定できるとしたら、以下のようなプログラムになるでしょう。

Sub サンプルマクロ()
    a2486820 = 20
    a2486788 = 10
    a2486756 = a2486820 + a2486788
    a2486740 = a2486820 - a2486788
    a2486724 = a2486820 * a2486788
    a2486708 = a2486820 / a2486788
    MsgBox a2486820 & " + " & a2486788 & " = " & a2486756
    MsgBox a2486820 & " - " & a2486788 & " = " & a2486740
    MsgBox a2486820 & " * " & a2486788 & " = " & a2486724
    MsgBox a2486820 & " / " & a2486788 & " = " & a2486708
End Sub

すごく分かりにくいですよね。
我々プログラマーは、データを保存して、再利用したいだけなのに、いちいちどこに保存するか番号で指定しないといけないなんてうんざりしますよね。ですから、保存場所の管理はコンピューターに任せて、我々はデータを名前で呼びましょう。

これが変数です。

さて、ここから先にまたオブジェクト変数という手ごわい奴がいますが、これは別の記事で触れているので割愛します。

さらに詳しいメモリの働きについては、こちらをご参照ください。
thom.hateblo.jp

上記を理解したうえで、オブジェクト変数について以下の記事を読んでいただけるとよく理解できるのではないかと思います。
thom.hateblo.jp

以上。

あとがきコメント
今回の記事は前回書いた以下の考察を実際の説明に生かしてみようと思って書いた。

thom.hateblo.jp


しかし、たとえ話を切り出すタイミングが難しく、それほどうまくいったとは思っていない。
むしろ本質的な変数の機能の説明の後にあえてたとえ話を持ち出す必要もなかったかなという思いが強いのだけれど、何かしらイメージとして形と結びついていたほうが記憶にとどまりやすいかもしれないという考えもあり、結局、形をもつ箱やメモ、タグなどのたとえを紹介することにした。
もう少し表現や説明順序を洗練させていきたいと思う。

変数・関数・オブジェクトが「いまひとつ分からない」という感覚の正体を考察

プログラミングでは日常生活であまり耳にしない言葉がたくさん出てくる。

たとえば、変数・関数・オブジェクトなど。

説明を聞いてスッと理解できる方もいれば、なかなか意味が分からずに苦労したという方もいるだろう。

さっぱり分からないということはない。そして使ってみろと言われて使えないわけでもない。…にもかかわらず、いまひとつ分からない。なんかもやもやする。スッキリしない。

では一体なにが分からないのだと聞かれても困る。なんだかよく分からないとしか答えようがない。そんな経験はないだろうか。

というか、多くの人がこのように感じながら日々使っているうちに慣れてしまった方が多いのではないかと思う。

そして他の人に教えるときも「慣れるしかない」などとしたり顔で言ってしまうんだ。

でも、本当に慣れるしかないのだろうか。

「慣れるしかない」というのは、場合によっては自分がうまく説明できないのを誤魔化したように聞こえることがある。完璧に説明できなくてもいいから、もう少し納得度の高い答えを示せないだろうか。

それで私はここ数日、この「いまひとつ分からない」という感覚の正体をもう少し正確に言語化できないかと悩んでいた。まず敵を知らなければ対処のしようがないからだ。

それでいろいろ調べていたところ、認知心理学の分野で「概念形成」という言葉を見つけた。

何かを理解したというのは、言い換えると頭の中で概念が形成されたということで、「いまひとつ分からない」というのはつまり、概念形成がうまくできていないという状態なのではないか。

ではどうすればうまく概念を形成することができるのか。
その前に、概念という言葉をはっきり定義しておこう。概念を形成しようというのだから、肝心のそれが何なのかはっきりわかっておく必要がある。

ここで国語辞典を取り出してくる。

岩波国語辞典より

概念-同類のものに対していだく意味内容。
ア)同類のもののそれぞれについての表象から共通部分をぬき出して得た表象。
イ)対象を表す用語について、内容がはっきり決められ、適用範囲も明確な、意味。
ウ)俗に、複雑なものに対する大まかな認識内容のこと。

このうち(ウ)の意味で理解している方も多いと思うが、今回扱うのは(ア)と(イ)である。

で、またここでややこしい言葉が。
表象(ひょうしょう)とは。。。

岩波国語辞典より

現在の瞬間に知覚してはいない事物や現象について、心に描く像。イメージ。

い、、一応知覚も調べておくか。。

岩波国語辞典より

感覚器官を通じて、外界の事物を見分け、とらえる働き。視覚・聴覚・嗅覚・味覚・触覚など。

つまり、たとえば「変数」に対する概念を形成するとは、数ある「変数」に対する脳内イメージのうち、共通するイメージを抜き出したものを脳内に形成するということを指す。

よく変数は箱に例えられる。私は箱だと説明されて「なるほど箱か」と何も疑問に思わなかったので良かったけれど、一定以上の人がこの説明で躓くらしい。

そこでしばしば、「変数=箱」モデルはあまり上手い例えではないとの指摘がなされる。そしてメモに例えたり、名札に例えたり、実際にどうなっているのかメモリの動きを説明したりという試みが行われている。

しかし先ほどの概念形成の定義を考えると、どれが良いということではないような気がしてきた。

もう一度言うと、「変数」に対する概念を形成するとは、数ある「変数」に対する脳内イメージのうち、共通するイメージを抜き出したものを脳内に形成するということを指す。

変数とは箱であるという説明をいくら繰り返しても、共通するイメージは「箱」そのものである。一方、メモであるという説明を繰り返しても、共通するイメージは「メモ」そのものである。

箱という説明で躓いた人がメモという説明でなるほど!となるのは、メモに例えるのが箱に例えるよりも優れているということではなく、複数の事例を見ることで共通する特徴を捉えやすくなるからだろう。

変数がハコにもメモにも似ているのであれば、まず消去法で変数が立方体である可能性は消える。

では具体的なメモリの動きを説明するのはどうか。複数の人に説明してみて、これは変数の概念を形成するのに極めて効果的だった。しかしこれも、ハコ・メモといった既存の説明を受けた後でのことであるから、メモリの説明単体で変数を説明するのは難しいかもしれない。

それに変数におけるメモリの働きはたまたま実装がそうなっているということにすぎず、変数の本質かと言われるとやっぱり違う気がする。

プログラミングにおける変数とは本来、色も形もない純粋な概念なのだろう。だから人に説明するにはハコやメモといった比喩や、実装という現実を使って、事例の形で言語化するしかない。

よく分からなくても使っているうちに慣れるというのは、つまりコーディングを通じて脳内にたくさんの事例をため込めば自然と概念が形成されるということ。

だから、「いまひとつ分からない」から「はっきり理解できる」に移行するために「慣れる」という手段は有効であるが、「慣れるしかない」わけでもない。

いろんな切り口から説明すれば共通項が概念として形成されるし、たとえ話だけで理解されない場合は具体的な実装に踏み込んでみるとスッキリ理解していただけることが多い。

それでもうまく説明できないときは、自分の力不足であることを認めたうえで、申し訳ないけれど実際に使いながら慣れてくださいというと好印象だと思う。

マクロ作成を安請け合いしないという選択

「こんだけVBAの記事を書いておいてよくもまぁ」という声が聞こえてきそうなタイトルである。
しかし安請け合いがよくないというのは常々思っている。

執筆のキッカケになったのはこの記事。
akashi-keirin.hatenablog.com

以下、序文の引用。

最近、

自分には一文の得にもならない、純粋に他人のためのマクロ

を作らされることが多くなって

これを読んで思わず、

。。これ、プログラマーの仕事じゃね?

と突っ込んでしまった。
ここで言ってるのはプログラムを組める人全般のことじゃなくて、プログラムを開発する目的で正式に雇用された正規プログラマーのこと。

どうやらドツボにはまってしまったようだ。

私も似たような経験をしたことがある。ただ私は業務請負の客先常駐という形態で、お客様の組織が何度か大きく変わったので、幸いにも私はリセットする機会があった。しかしあまりマクロ作成を安請け合いしてしまうと確かにそういうことになるというのは良くわかる。

ただ私の考えは上で紹介した記事ほどマクロ作成を請け負うことを忌避しているわけではない。

まずいのはあくまで「安請け合い」であると考える。

本質的な問題は、依頼者が「減るもんじゃあるまいし、ケチなこと言うな」と考えている点だ。

時にマクロを作る側でさえ「減るもんでもないし、できるんだから、やってあげよう」と考えることがある。

「減らない」という考え。

ここにすべての元凶があるのではないだろうか。

そもそも他の皆が自分の好きなことや遊びに費やしている時間を貴方はプログラミングの習得に費やしたのだ。他の皆が自分の好きなことや遊びに費やしている給料の一部を貴方はプログラミングの書籍購入に充てたのだ。

今貴方が手にしているその能力は、すでに自分の時間やお金を費やした結果である。

もちろんそれだけではない。プログラミングというのは頭脳労働なのだ。実際、頭脳労働は気力を消費する。ゲームでも魔法を使うとMPが減るだろう。気力は定量的に計ることはできないけれど、確かに減るのだ。要するに疲れるのだ。

知識や技術、気力という見えないコストをきちんと理解してもらうためには、元手はタダではないと何度も根気よく説明しなければならない。

普段から、「さっさと帰る日は書店に立ち寄って勉強しているのだ」と吹聴しておこう。実際にしている日も、そうでない日も。
依頼仕事が遅れているときは、「昨日家でもいろいろ考えてたけれど、なかなか思うようにできないのだ。もう少し待ってほしい」と詫びておくとよい。実際に苦労していても、完成の目途が立っていても。

私は単に趣味と一致しているのでプログラミングの学習に費やす時間を苦労とは思ってないけど、実際に見えないところにコストがかかってるのは嘘ではないので、「あいつは見えないところで苦労している」という風に見せておかないと、「能力があるのにケチな奴だ」と思われる。

何の努力もせずに評価されないのはともかく、実際にやってるのだから、どんどんアピールして良い。
自慢のためではない。コストを理解してもらうためである。早く帰っても恨まれないためである。

勘違いしてほしくないのは、極力出し惜しみをしろと言いたいわけではない。

私だって毎日の3時間の仕事が5分になるって言うなら喜んで手伝う。それで皆が楽になるなら大したことではない。
価値ある仕事をしたときは、それ自体が心理的報酬になる。

しかし人間はどこまでも楽をしたがる生き物である。
「マクロってすごい。」こんな風に一度魔法を見てしまうと、ひょっとしてあの仕事も楽になるのではないかと次々とアイデアが浮かぶようになる。次第になんでもかんでも自動化したがるようになる。たとえ、ひと月に1度、30分で終わる仕事であっても。

その自動化は、自分が費やしてきたもの・これから費やすであろうものに見合った価値ある仕事だろうか。
傍から見てそれほど大変な仕事に見えない場合、単に依頼者が楽をしたいために貴方に甘えているだけかもしれない。
何でもかんでも引き受けるわけではないと、お断りしたほうが良い。あるいは自分の抱えている別の仕事と引き換えに引き受けるという手もある。

いずれにしても、つまらない仕事は安請け合いしないことである。請ければ依頼人は喜ぶが、貴方は内心つまらないと思っているのでイライラする。精神的にもよくないし、そのような仕事はたぶん、ビジネス的にも特にメリットはない。

Pythonスタートブックのレビュー ~徹底して比喩表現にこだわった良書

先日から気になっていた書籍 Pythonスタートブックを買ってきたのでレビューしようと思う。

Pythonスタートブック

Pythonスタートブック

ただ、Python学習の学習が目的ではなく、あくまで「プログラミング入門」として読んでいる。
プログラミング初心者に対してVBAの解説を行う際のヒントにするためだ。

Amazonレビューではかなり高評価が多く、唯一の星1つはKindle版がうまくダウンロードできなかったという書籍の内容とは関係のないものだ。(2017年3月7日時点)

星3つの中には「テーマが時代にそぐわず面白くない。説明がくどい、うざい」という辛口レビューも見受けられたがこれは上級者の意見。上級者にとって自明の事実まで親切丁寧にかかれているのでお節介に感じるのだろう。

逆に難しい・わからないという意見は見当たらず、プログラミング初心者からは概ね5つ星。ということは、まったくの初心者をターゲットにした書籍としては大成功ということになるか。

さて、本書であるが、とにかく徹底して比喩表現を用いている。

私は比喩によるミスリードがますます初心者を混乱させるという例をいくつも知っているので、最近は比喩表現の使用に慎重だった。ひとつ例を挙げると、以前別の方にプログラミングを教わったけど今ひとつ分からないという方に、「変数って浮いてるんですよね?」って質問されたことがある。

浮いてるって。。どこに!?
たぶん箱モデルでうまく伝わらず、教える方もメモリに記録されているということを知らないかあるいはうまく説明できず、目に見えないけどどこかに浮かんでいるといった曖昧な説明でお茶を濁したものと思われる。

浮いているという説明でますます混乱されていたので、メモリの仕組みからきちんと説明したところスッキリ理解していただけた。

このような体験があるため、私は比喩表現を避け、徹底して「事実・実態」の説明に拘るようになった。

ただ最近、事実に拘るあまり逆に難しくて分かりにくい解説になっているのではないかと思うことがある。

比喩表現には誤解のリスクもあるが、初めて出会う概念を既知の概念と関連づけて理解できるという優れた一面もある。適切に用いれば極めて有効な説明の手段である。いたずらに敬遠せず、適宜活用していこうということを悟ったのである。

それでPythonスタートブックである。もう一度言うが、とにかく徹底して比喩表現を用いている。ごてごての比喩表現。

つまりアンチ比喩に大きく振り切れた針をもとに戻すには、絶賛比喩中のこの書籍がよい薬になるのではないかと思って買った。

この書籍ではまずプログラミングの構成要素を、「道具と材料」に見立てて解説している。
以下に一部引用する。

たとえば、木製のイスを作ろうと思ったら、材料になる木と釘、道具としてはノコギリや金槌が必要です。

プログラミングにおいて基本となる材料とは、文字列や数字といった”データ”です。

Pythonにもプログラミングに役立つ道具がたくさんあります。その1つが、関数(かんすう)と呼ばれるものです。

ためしにプログラミング未経験者にこの道具+材料でプログラミングというものを説明してみたところ、すこぶる理解が良い。なるほど、これが比喩の力か。

まず初心者にとって、変数とか関数とか言われてもどれがどれか結びつかないのだ。何せ覚えたての言葉を使って初めて見るコードの構造を把握しようとしているのだ。

f:id:t-hom:20170308002345p:plain
無理とは言わないが難しいことである。

よく慣れ親しんだ言葉でまずはざっくりとプログラムの構造を把握するという手法は有効だと思う。
f:id:t-hom:20170308002422p:plain

道具+材料の表現はこれからもちょくちょく活用していきたい。

あとfor文、While文、If文、エラー処理などの制御を電車の路線に例えているのは面白かった。

この書籍は、徹底した比喩表現にこだわった良書だと思う。

あとがき

なんでまたPythonの本を手に取ったのかというと、ツイッターでコーヘー氏とインストラクターのネタ帳の伊藤さんがPython入門書を褒めてたのを見たので、そんなに良いなら私もソレ買って読もうと思って。

ところが完全に記憶違いで、別の本を買っちゃったという話。

お二人が良いといってたのはこちらの書籍。

独習Python入門――1日でプログラミングに強くなる!

独習Python入門――1日でプログラミングに強くなる!

たしかなんかカラフルな表紙だったなーという程度の記憶でAmazonPython入門書を検索して、「ほう、たしかに評価が高い、これに違いない」という顛末。

まぁ結果的には正解だったと思っている。
特にアンチ比喩に傾きつつあった自分には良い薬になった。

VBA Excel表をHTMLのtableタグに変換するマクロ

先日メインサイトにXlRgbColor定数の一覧を掲載した。

色順に並べたXlRgbColor定数の一覧表 - You.Activate

今回はこれを作る時に使用したExcelマクロを題材に、VBAExcel表からtableタグを出力する方法について記す。

まずHTML化する前のExcel表を用意しておく。

今回使用したのはこちら。
f:id:t-hom:20170306221642p:plain
※7色で画面から切れてるが、実際は137色ある。

これを、冒頭で紹介したようにWebページで表示させるには、HTML形式に変化しなければならない。
具体的には、Excel表を以下のようなHTMLに変換する必要がある。

<table>
<tr>
<th>No</th>
<th></th>
<th>名前</th>
<th>RGB値</th>
<th>説明</th>
</tr>

<tr>
<td class="col0">1</td>
<td class="col1" style="background-color:#800000; width:30px;"> </td>
<td class="col2">rgbMaroon</td>
<td class="col3">128, 0, 0</td>
<td class="col4">栗色</td>
</tr>
<tr>
<td class="col0">2</td>
<td class="col1" style="background-color:#8b0000; width:30px;"> </td>
<td class="col2">rgbDarkRed</td>
<td class="col3">139, 0, 0</td>
<td class="col4">濃い赤</td>
</tr>

<!--以下略 -->
</table>

Excel表をhtml化するWebツールもあったが、セルの色までは再現してくれなかったので自作方法を覚えておくと役に立つかもしれない。

さて、タイトルでtableタグに変換するマクロと書いたが、実際にマクロで出力するのはtableの中身である、tdタグ
tableタグとthタグ(テーブルのヘッダー)もマクロで作ることはできるが、それほど手間でもないので今回は手入力とした。

マクロと手入力の分担は以下の通り。

<!-- ここから手入力 -->
<table>
<tr>
<th>No</th>
<th></th>
<th>名前</th>
<th>RGB値</th>
<th>説明</th>
</tr>
<!-- ここまで手入力 -->

<!-- ここからマクロ -->
<tr>
<td class="col0">1</td>
<td class="col1" style="background-color:#800000; width:30px;"> </td>
<td class="col2">rgbMaroon</td>
<td class="col3">128, 0, 0</td>
<td class="col4">栗色</td>
</tr>
<tr>
<td class="col0">2</td>
<td class="col1" style="background-color:#8b0000; width:30px;"> </td>
<td class="col2">rgbDarkRed</td>
<td class="col3">139, 0, 0</td>
<td class="col4">濃い赤</td>
</tr>
<!-- ここまでマクロ -->

<!-- 以下略 -->
<!-- tableの閉じタグも手入力 -->
</table>

つまり実際にマクロでは、以下テンプレートの[No]、[色値]、[色名]、[RGB値]、[説明]をループで次々と変更しながら出力すれば良いということ。

<tr>
<td class="col0">[No]</td>
<td class="col1" style="background-color:#[色値]; width:30px;"> </td>
<td class="col2">[色名]</td>
<td class="col3">[RGB値]</td>
<td class="col4">[説明]</td>
</tr>

※tdタグにそれぞれclassを付けたのは、列ごとに後からフォントサイズを調整しやすくするため。

さて、ここから実際にマクロを作成していく。

検証段階ではとりあえずDebug.PrintでHTMLを出力させるが、イミディエイトウインドウへは200行までしか出力できず、切れてしまう。
後で簡単にテキストファイルへの書き出しに切り替えられるよう一旦WriteLineというプロシージャを作っておこう。

Sub WriteLine(message)
    Debug.Print message
End Sub

こうすればマクロが完成した後にWriteLineの中身を書き変えることで出力先をテキストファイルに切り替えられる。
※こうした保守性の観点からのプロシージャ分割も重要。

次に今回はXlRgbColorの色見本を扱うので、テーブルのセルの背景色をそれぞれ個別に指定する必要がある。
出力するのはHTMLなので16進数の色表記を用いる必要があるが、Excel表では単なるRGBのLong値なので、これを16進数の文字列に変換するための関数もあらかじめ作っておく。

以下がその関数。RGB値を渡すと、HTMLで使用する16進数に変換してくれる。

Function RGBToHTMLColor(color_rgb)
    Dim r: r = color_rgb \ 256 ^ 0 Mod 256
    Dim g: g = color_rgb \ 256 ^ 1 Mod 256
    Dim b: b = color_rgb \ 256 ^ 2 Mod 256
    Dim hexR: hexR = Right("0" & Hex(r), 2)
    Dim hexG: hexG = Right("0" & Hex(g), 2)
    Dim hexB: hexB = Right("0" & Hex(b), 2)
    RGBToHTMLColor = "#" & hexR & hexG & hexB
End Function

そして、出力したい表のあるエクセルシートのシートモジュールに以下を張り付けて実行すると、イミディエイトウインドウにHTMLが出力される。

Sub 表をHTML化()
    For= 2 To 138
        WriteLine "<tr>"
        For= 1 To 5
            If= 2 Then
                Dim backGroundColor As String
                backGroundColor = RGBToHTMLColor(Cells(, 4).Value)
                WriteLine "<td class=""col1"" style=""background-color:" _
                    & backGroundColor & "; width:30px;""> </td>"
            Else
                WriteLine "<td class=""col" &- 1 & """>" _
                    & Cells(,).Value & "</td>"
            End If
        Next
        WriteLine "</tr>"
    Next
End Sub

注意:(私にとっては)単発ものなので、変数宣言もしてないしループ回数もリテラルべた打ち。良いコードとは言えないので、こちらを参考に汎用的なマクロを作る場合は適宜きちんと書いて。

さて、イミディエイトウインドウでは200行以上出力すると古いものから順に消えてしまうのでテキストファイルへの出力に変更したい。
ここでFileSystemObjectを使用するので、Microsoft Scripting Runtimeを参照設定しておく。

そしてWriteLineを以下のように書き換える。

Sub WriteLine(message)
    With New FileSystemObject
        Dim ts As TextStream
        Set ts = .OpenTextFile("c:\work\writeline.txt", ForAppending, True)
        ts.WriteLine message
        ts.Close
    End With
End Sub

※みなさんが利用する場合、パスは適宜変更が必要。また、追記モードで開くので試行錯誤する場合はいったんファイルの中身を消す必要がある。

これでtableの中身がテキストに出力されるので、あとは手入力のtableタグの中に張り付けるだけ。

VBA プロシージャのオーバーロード機能(もどき)を自作する

今回のネタは@mmYYmmddさんのつぶやきから生まれた。感謝。

さて、オーバーロードとは。
ふつう、Functionが取れる引数の数や型は固定されていてあまり自由が利かないのだが、引数の数や型によって処理を振り分けたい場合がある。
VariantやOptional、ParamArrayを活用することでこのような処理も可能であるが、一つの関数で処理を振り分けるとごちゃごちゃしてしまいメンテナンス性が落ちる。

これを解決するのがオーバーロードである。オーバーロードとは、引数の数もしくは型が違えば同名のプロシージャをいくつでも作成できる機能で、JavaC#C++などの言語には実装されている。
これを使えばあくまで別の関数として作れるので、複雑にならずにすむ。

残念ながら、VBAにその機能はない。そこで今回はオーバーロード(もどき)を実装してみたい。
業務コードでこれをすると複雑になるだけなので、ライブラリコードでしか使い道はないけど。

まず引数の型を判定するための関数をFunctionプロシージャで作成。

Function GetArgTypeString(ParamArray args()) As String
    Dim ret As String
    For Each x In args
        Select Case True
        Case IsMissing(x): ret = ret & "M"
        Case IsArray(x): ret = ret & "A"
        Case IsObject(x): ret = ret & "O"
        Case TypeName(x) = "String": ret = ret & "S"
        Case IsDate(x): ret = ret & "D"
        Case IsNumeric(x): ret = ret & "N"
        Case Else: ret = ret & "U"
        End Select
    Next
    GetArgTypeString = ret
End Function

この関数にいくつか引数を渡すと、文字列で型が返ってくる。
たとえば、1, #2017/3/5#, "Hello" の順で渡すと、NDSとなる。Number、Date、Stringである。

次に、たとえばAddという関数を作りたい場合、Addに先ほどの文字列をつけた名前で関数を作る。
タイプごとに4つ用意した。

Function AddSSM(a, b)
    AddSSM = a & b
End Function
Function AddSSS(a, b, c)
    AddSSS = a & b & c
End Function
Function AddNNM(a, b)
    AddNNM = a + b
End Function
Function AddDNM(a, b)
    AddDNM = a + b
End Function

そして、渡された引数によって実際の関数へ処理を引き渡すための、窓口となる関数を作る。
これが本来呼び出したいAdd関数。

Function Add(a, b, Optional c)
    Dim typeString As String
    typeString = GetArgTypeString(a, b, c)
    Add = Application.Run("Add" & typeString, a, b, c)
End Function

あとはメインコード。

Sub Main()
    Debug.Print Add(1, 2)
    Debug.Print Add("1", "1a")
    Debug.Print Add(#3/5/2017#, 1)
    Debug.Print Add("A", "B", "C")
End Sub

実行するとこのとおり。

 3 
11a
2017/03/06 
ABC

まぁ使い物になるかどうかはわからないが、私が類似の問題を抱えたときにアイデアの叩き台になればと思い、ここに残しておく。

念押しするが、アプリケーションコードでコレは余計複雑になるだけなのでやめておいたほうが賢明。ただライブラリを書く目的ならこういうのもアリかなと思う。

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。