t-hom’s diary

主にVBAネタを扱っているブログです。

Amazonでレビューを書く意義とその書き方について

最近、良い買い物をしたら出来るだけAmazonでレビューを書くようにしている。
その理由は、将来的に私がさらに快適な暮らしをしたいからだ。

レビューを書く意義

Amazonでレビューを書くことと、快適な暮らしがどう繋がるのかを以下に図示してみた。

f:id:t-hom:20180722230701p:plain

つまり気に入ったものにちゃんと高評価を付けることで他の買い手が付き、そのお金が作り手に還元されれば次の商品を開発する資金になり、私は進化した商品を再び買うことができる可能性が高まるということ。

私が一票入れたくらいで変わらないと思うかもしれないが、まだあまりレビューが付いていない段階での高評価の効果は馬鹿にならないし、沢山レビューがついていたとしても、異なる視点からの一票はとても貴重なので無駄ではない。

悪評価と高評価の性質

レビューは商品を買ってひどい目にあった人が腹いせに書くというパターンが最も多いように思う。逆に、多くの人は商品に満足したら、わざわざレビューなんて書かないのではないだろうか。このことから、基本的に悪評価は集まりやすく、高評価は集まりにくい傾向にある。

良い商品をわざわざ良いと評価する人が居なくなれば、勝手に集まってくる悪評価に負けてしまう。
これも気に入った商品をわざわざレビューする理由である。

低評価レビューのアンチパターン

低評価レビューには全く役に立たないばかりか、不当に商品の価値を貶めるレビューも多く見かけるのでアンチパターンとして挙げておく。

アンチパターン その1) 客観性がない

単に個人の愚痴を述べているだけで、客観性がないもの。
例) 全く役に立ちませんでした。思っていたのと違いました。説明が分かりにくい。使いにくい。 等

ただし主観的レビューも役に立たないわけではない。
例えば低評価が大部分を占め、ほぼ全員が「使いにくい」と言っていたら、それはまあ実際に使いにくいんだろう。
主観的な言葉も数が集まれば信憑性を帯びてくる。

アンチパターン その2) 作り手の責任ではない

例) Kindleで購入したけど何度やってもダウンロードできない。自分が(勝手に)想定していた用途に向かなかった。(測らずに買ったくせに)サイズが合いませんでした。 等

アンチパターン その3) 情報不足

使用環境などが明記されてなくて「すぐに壊れた」とか、サポートの品質が悪いとかいうケース。参考になりそうでイマイチなのがこれ。
特にサポート品質に関するクレームは具体的なエピソードが無いと判断できない。特に商品に腹を立てている顧客は、どんなに丁寧に応対しても聞く耳を持たないから。

たとえば家電を買って数日で壊れた場合、多くの顧客は「直ちに交換品を送ります!」という対応を期待する。あまりハズレを引いたことのない方ならなおさらだ。
でも残念ながら、最初から電源が入らないなどの明らかな初期不良を除き、一般的には修理対応になる。それをもって、そのメーカーのサポートが他より劣っているということにはならない。

良いレビューとは

とりわけ低評価レビューを書く場合は、具体性と客観性が重要だと思う。どういう使い方をして、どんな場面で困ったのか等、状況が具体的であるほど、読んだ人が自分のケースに当てはめて想像しやすくなる。

高評価レビューの場合、単に感じたことを主観的に書いても良いと思う。その商品を気に入ったという投票は、自動的に集まってくる低評価に拮抗する為にとても役に立つ。※ただしステマを除く

もちろん、具体的に褒めるポイントがあれば書いたほうが良いし、活用のシチュエーションが分かるようなエピソードがあればより良いレビューになると思う。

星を付ける基準

我々は商品がどれくらい良いかを星の数で判断していると思うけれど、よくレビューを読んでみると、星を付ける基準って人によって随分と異なることがわかる。
ある人は特に問題がなければ5を付け、減点法で評価しているし、ある人は問題なければ3、感動したら5という風に人によって評価が定まらない。

レビューする立場だと普通を3とする人が多いけど、買う側からしたらどうだろうか。正直評価3の商品って欲しくないだろう。特に問題のない商品なのに、Amazonで「普通」の評価が付くことで確実に買いたくなくなる。だから商品に特に問題が無かったなら最低でも4は付けて欲しいと思う。(個人の願望です。)

私の場合は「その商品が生産されなくなったらどれくらい困るか」の度合いで星の数を決める。
これは冒頭で述べた、将来的に私がさらに快適な暮らしをしたいからと関係している。

その商品に類似品が無く、全体的にとても気に入ったとしたら、たとえ一つくらい気に入らない点があったとしても私は星5つを進呈する。
残念な点は本文で述べれば良いし、売れなくて生産ストップする方がはるかに困ったことになる。

特別に気に入った商品は、もともと7つ星だと思えばいい。

おわりに

ネットが登場してから消費者の声が大きな力を持つようになった。実際にAmazonで買わなくても、レビューだけは見るという人も多い。

ユーザーは少しでも気に入らないと容赦なく商品をこき下ろす。どんなに良いものでも最初に低評価がついてしまったら苦境に立たされる。
だからこそ、良いと思ったものは、わざわざ時間を割いて賞賛を送りたい。そうしないと将来自分が欲しいものが手に入らなくなってしまうのでは。

IT技術の独学で入門難民にならないために大事なこと

IT技術を独学するにあたって、挫折しないために必要なことは何だろうか。
挫折といっても原因は様々だけど、大きく分けて「続かない」と「始められない」の2種類がある。
今回紹介するのは「始められない」を避けるために大事なこと。

ある技術の学習を始められない人々を、入門難民と呼ぶことにする。かくいう私も、Androidプログラミングの入門難民である。あとAI技術の入門難民でもある。

入門難民にならないために大事なことを3つ紹介する。

  1. PCスペックはなるべく良いものを
  2. 書籍は発売されたらすぐに買う
  3. ハズレた書籍にこだわらずにさっさと次を買う

詳しく紹介しよう。

PCスペックはなるべく良いものを

これは、プログラミング言語を学ぶだけなら特に当てはまらない。
ただしAndroid開発や機械学習となると、それなりのスペックが必要になるので、もしそういった技術分野に興味があれば、PCスペックはなるべく良いものを揃えておいた方が良い。
かく言う私も長年Androidプログラミングに入門できないでいたのは、PCスペックが低くてAndroid開発環境や仮想マシンがまともに動かなかった為。

とはいっても何を買えば良いのか分からないと思うので。参考までに私のPCスペック。

CPU Intel Core i5 7500 3.4GHz
グラフィック NVIDIA GeForce GTX 1050 Ti
メモリ 8GB

いまのところ、これで開発系は何も困ってない。

書籍は発売されたらすぐに買う

これも、プログラミング言語を学ぶだけなら特に当てはまらない。言語そのもののアップデートはそれほど早くないし、その時々で発売されている新しい書籍を手に取れば、概ね問題なく入門できる。特にVBAなんて10年以上ほとんど変わってない。

問題は環境の変化が激しいAndroidクラウドプラットフォームなど、比較的新しい技術である。
発売から半年もすれば環境がアップデートされてて、まったく書いてあるとおりに進まない。右も左もわかってない入門の段階で、書籍に書いてある通りに進めないというのは致命的で、入門難民まっしぐらである。私は最近Google Cloud Platformで入門難民となった。

発売直後は良書だったに違いない。しかし、どんどん環境がアップデートされるので書籍の方が追い付かないのだ。
だから新刊が出たらすぐに飛びついて、とりあえず入門してしまうこと。入門段階さえクリアすれば、いろんなことが分かるようになるし、分かってしまえばその後の学習は古い書籍も活用できる。

ハズレた書籍にこだわらずにさっさと次を買う

これはVBAにも当てはまるし、すべての技術書に言えることだけど、当たりハズレはある。
合うか合わないかという問題かもしれないし、単に文章が下手で頭に入ってこないのかもしれない。あるいは読解力が不足しているせいかもしれない。
いずれにしても、合わない本で無理に頑張るのは挫折への第一歩だ。

お金はかかるけど、独学で良書に巡り合うためには次々に本を買った方がいい。
スタート地点にも立てない入門難民になるくらいなら、多少の出費なんて大したことない。
それに物事は多面的にとらえて初めて意味が分かるので、一冊よりも複数冊を読んだ方が違う切り口から学べるので理解が早い。
書籍の購入を躊躇しているうちに情報は古くなり、ますます挫折しやすくなる。

まとめ

やる気さえあれば、特定の技術にいつでも入門できると思ったらそれは間違いである。
技術書は高い。いつか買おうと思って後回しにしてしまう。でも、実は書籍にも賞味期限があって、後回しにしているうちにどんどん鮮度が落ちてしまう。だから、発売直後が一番安い。値段は変わらないのに、時間が経つほど食える部分が減っていくから。

機を逃すと入門難民になってしまうので、すぐにでも行動しよう。

私は今でも後悔している。
あの時、最新のPCを持っていたら。
あの時、気になった本を買っていたら。

十分なスペックのPCを手に入れた今は、良書の発売を待ちわびている。今度は逃さない!絶対に!

はてなブログ(このサイト)を常時SSL化したので備忘録

本日このブログを常時SSL化したので、備忘録も兼ねてここに記しておく。
切り替えそのものはダッシュボードから簡単にできるのでまぁ簡単。
問題はその後。

まぁただ、以下の広野さんの記事を読んで、面倒な後処理が発生することは想定内ではあった。心の準備ができたので感謝!
www.mayoinu.com

目次

SSLとは

SSLとは、Secure Socket Layerの略で、暗号通信に使われる技術のことだ。
これをWebサイトの配信に用いたものがhttps。最後のエスがセキュアであることを示す。

これまでの常識では、単に情報を発信するページは暗号化されていないhttpで行い、訪問者にフォーム入力させるようなページ(とりわけクレジットカード番号や個人情報など)では、そのページだけSSLに対応したhttpsで通信するのがセオリーだった。

しかし米国NSAがhttpの性質を利用して、本来無害なサイトへのアクセスを改ざんしてスパイプログラムをインストールさせているという事実がエドワード・スノーデン氏により暴露され、以降は単に情報を発信するだけのサイトであっても全てのページをhttpsで通信させる方法(常時SSL)が常識化しつつある。

アドレスバー表示について

サイトがhttpsに対応して通信が暗号化されているとき、ブラウザのアドレスバーがそれぞれ次のように変化する。

Google Chromeのアドレスバー表示

通信が保護されている場合
f:id:t-hom:20180707200005p:plain

通信が保護されていない場合
f:id:t-hom:20180707204326p:plain

Microsoft Edgeのアドレスバー表示

通信が保護されている場合
f:id:t-hom:20180707200041p:plain

通信が保護されていない場合
f:id:t-hom:20180707204537p:plain

このように、ブラウザによってどちらを明示的に表示させるかが違うので、SSL通信が上手くいってるかどうかは自分のブラウザのバージョンや仕様を確認したうえで判断が必要となる。

混在コンテンツについて

サイト本体がhttpsで暗号化されているとき、画像やCSSJavascriptなど、ページのパーツとして同時に読み込まれる資源へのアクセスがhttpだと、保護されていない通信という扱いになってしまう。

これらの資源はページ本体をリクエストした際にサブリクエストで同時にダウンロードされるため、資源への通信がhttp通信だと折角ページ本体をhttpsで通信していても暗号化されていない通信が混じってしまう。
これを混在コンテンツ(Mixed-Contents)といい、ブラウザによっては通信が保護されていないと表示されてしまう(あるいは保護された通信と表示されない)。

単なる他ページへのリンクがhttpになっている分には問題ない。なぜならそれは資源ではないので、同時に読み込まれることはないから。
ただし常時SSL化の流れが加速すれば、将来ブラウザでHTTPサイトへのリンクをクリックした際に警告が表示されるなんてことになるだろうと私は予測している。

はてなブログ全体のhttps化

冒頭で述べたように、まずはダッシュボードの詳細設定で切り替えるだけ。ただし元のhttpには戻せないので注意。

それからデザインでサイドバー等に資源リンクがあればhttpをhttpsに書き換えるか、http:を消す必要がある。まぁ自分でカスタマイズした方であればそのあたりは大丈夫かと思う。HTMLタグだと単純にhttp:を消して//からアドレスを始めればサイトと同じ方式で通信してくれるので便利。※はてな記法でこの方法が使えないのが残念。

個別の記事のhttps化

これが一番大変。上手く行かないパターンは2つある。

保護されていない通信になるパターン

Amazon等のリンクや古い記事の画像等がうまくhttps化されてない。
ただ通常は画像やAmazonリンクの張替えまでは不要で、ページを編集して保存するだけで大抵の場合は直る。
編集といっても何も変更する必要はないのだが、それだと保存できないので、適当な場所に半角スペースでも入れて、更新ボタンが有効になったらスペースを消して更新すれば良い。

稀にAmazonリンクで古い商品を指してる場合はこれで治らないので最新の商品リンクを探してきて張り替える。

埋め込み資源が空白になるパターン

過去記事や他のブログを埋め込みリンクにしてる場合、資源としてサムネなどを取りに行くのでそこはhttpのままになっている。
Microsoft Edgeで試したところ、セキュリティ保護は問題なかったけど埋め込みコンテンツが取得できておらず真っ白。
つまり、http通信部分をカットしてるからセキュリティ保護が問題ないだけ。

自分の過去記事も含めて真っ白なので残念。
埋め込みコンテンツの中身をちゃんと表示させようと思ったら、httpsに書き換えていかないといけない。

埋め込む相手がhttps対応してない場合は?

試したところ、埋め込むコンテンツがはてなブログの場合は、対象のブログがhttps化されてなくても、httpsと書いて大丈夫っぽい。
それ以外の場合は逐一httpsに対応してるかどうか調べて対応させていったけれど、httpしか無いコンテンツは変更できない。でもなぜかそれは書き換えなくても表示が上手く行ってるので謎。

Mixed-Contentsが存在する個別記事をスクリプトで探す方法

全ページ開いて。。なんてやってられない。
以下のサイトで良さげなスクリプトが紹介されているのでこれを活用させていだいた。
tech.innovation.co.jp

ただこのサイト、ソースコードが画像になっててとても残念。。
コピペできるようにこちらに引用再掲させていただく。
これをerror_scan.jsというファイル名でc:\workなど分かりやすいところに保存しておく。
※私の場合はe:\scriptに置いた。

//出典:https://tech.innovation.co.jp/2017/09/17/mixed-content-checker.html
var system = require('system');
var url = system.args[1];
 
if (!/^https?:\/\//.test(url)) {
	console.log( 'URLを指定して下さい');
	phantom.exit();
}

var page = require('webpage').create();

page.onResourceRequested = function(request) {
	if (request.url.substr(0, 8) !== 'https://'
      && request.url.substr(0, 5) !== 'data:') {
        console.log('[mixed content]', request.url);
    }
};

page.open(url, function(status) {
    phantom.exit();
});

次にこれを使うためにはPhantomJSというツールが必要。
PhantomJSを使うと、コマンドでWebコンテンツにアクセスできるようになるので、以下のサイトからまずこれを入手して任意のパスに解凍しておく。
http://phantomjs.org/download.html

PhantomJSのbinがあるフォルダを環境変数Pathに登録しておく。
f:id:t-hom:20180707213623p:plain

次にコマンドプロンプトでerror_scan.jsを保存したフォルダに移動し、
「phantomjs error_scan.js チェックしたいURL」とコマンドを打つ。
f:id:t-hom:20180707214011p:plain

上のように、mixed-contentsと表示されていたらHTTPS通信できてない資源があるということ。
問題ない時は何も表示されない。

次に、先ほど紹介したサイトにはPythonでSitemap.xmlを解読しつつ全ページスキャンするコードも書かれている。
こちらも画像なので、テキストで引用させていただく。
このスクリプトはchecker.pyというファイル名でerror_scan.jsと同じフォルダに入れておく。

# 出典:https://tech.innovation.co.jp/2017/09/17/mixed-content-checker.html
import re
import sys
import ssl
import subprocess
import urllib.request
from bs4 import BeautifulSoup

def getMixedContentError(url):
    msg = ""
    result = subprocess.getoutput("phantomjs error_scan.js " + url)
    for line in result.split("\n"):
        if ("[mixed content]" in line):
            msg += line + "\n"
    return msg

ssl._create_default_https_context = ssl._create_unverified_context
sitemap_url = sys.argv[1]

sitemap = urllib.request.urlopen(sitemap_url).read()
soup = BeautifulSoup(sitemap)

for url in soup.findAll("loc"):
    errorMessage = getMixedContentError(url.text)
    if (errorMessage):
        print("x " + url.text)
        print(errorMessage)
    else:
        print("v " + url.text)

Pythonのインストール方法はググっていただくとして、このスクリプトを使うにはbs4というライブラリをインストールする必要がある。
Python3.4以降に同梱されているpipというツールを使ってインストールするけど、Pythonをインストールしただけではpipにパスが通ってなかったので、環境変数にパスを追加しても良いけど、とりあえずpipのあるパスで実行することにした。

whereコマンドでPythonの場所を調べ、そこのScriptsフォルダに移動し、「pip install bs4」を実行。
f:id:t-hom:20180707215350p:plain

これでPythonスクリプトの方も実行できるようになった。

実行するにはコマンドプロンプトでchecker.pyを保存したパスに移動し、
python checker.py サイトマップのURL」を実行する。
f:id:t-hom:20180707215838p:plain

はてなブログサイトマップは「ブログURL/sitemap.xml」にある。
例えばこのブログなら、https://thom.hateblo.jp/sitemap.xml

そのxmlに更にページ番号がついたサイトマップが記載されており、実際にはこのページ番号付きのサイトマップをcheckr.pyに引き渡すことになる。
1ページ目はこんな感じ。
python checker.py https://thom.hateblo.jp/sitemap.xml?page=1

暫く待ってればpage1の中の全URLがチェックされるので、コマンド結果をコピペしてエディタに張り付ける。

f:id:t-hom:20180707220444p:plain
先頭にvと付いたURLは問題なかったということ。
先頭にxと付いたURLは混在コンテンツなので要修正。
Mixed-Contentsと表示されているものはページ内で具体的にどれがhttp通信なのかを表示している。

余談だけど、EmEditor(エムエディタ)だと、リンククリックでそのままページを開けるので楽。
また一度開くと赤くマークされるので完了チェックリストとしても使える。

修正が終わったら、サイトマップの次のページのURLをchecker.pyに引き渡して実行する。
python checker.py https://thom.hateblo.jp/sitemap.xml?page=2

これの繰り返し。
ものすごく面倒くさかったけどまぁなんとか全ページSSL化完了した。

7/18追記

私は記事のMixed-Contentsの洗い出しまでをスクリプトでやったけれど、以下のブログ記事ではスクリプトによる記事の更新について書かれていて、更にスマートに作業できそう。
blog.jnito.com

VBA Select Case文における条件式の短絡評価活用の是非について

VBA Select Case文における条件式の短絡評価活用の是非について述べる。

前提

以下の書籍で、Select Case文の条件式短絡評価について記載されているが、そのテクニックとしての是非についてTwitter上で疑問が上がっていた。

Select Case文のCase条件は、カンマで区切ることで複数記入することができる。
例) Case 条件1, 条件2, 条件3

このとき、一つでも条件を満たせばCaseにマッチする仕組みになっているが、左から順に評価され、マッチした時点で残りの条件は評価されなくなる。

例えば次のコードにおいて、Case文の2番目の式は数値をゼロで割るというコンピューターではエラーになる処理だ。

Select Case True
Case True, 1 / 0
    Debug.Print "成功"
End Select

しかしCase文は最初のTrueにマッチした時点で後ろの式を評価しなくなるので、結果的に1 / 0は処理されずにエラーにならない。
こういった処理のことを短絡評価またはショートサーキットという。

この記事では、Case文が短絡評価を行う性質をテクニックとして用いて良いかどうかについて私の意見を述べる。
また、Twitter上で、If文と比較した場合に文法上の整合性が取れていないという意見があったので、こちらも私なりに解説してみた。

結論

結論からいうと、私個人的にはこの性質をテクニックとして使って良いと思う。
ただ、積極的に推奨している訳ではなく、良いかまずいかといえば、別にまずくはないのでは?という印象。

より良い書き方はケースバイケースだと思うが、素直にIfをネストさせたほうが分かりやすいケースが大半かと思う。
私がこのテクニックを知ってから、これが極めて有効に機能するシチュエーションにまだ遭遇したことがないので、あくまで現時点の評価である。

使って差し支えないと思う理由

1) ロジックに影響を与えないから。
短絡評価でエラー回避を行ったところで、本来TrueになるものがFalseになったりすることはない。
つまりプログラムロジックに影響を与えるものではないので短絡評価するという事実を知らないプログラマーがコードを読んだ場合にもロジックを誤解させる弊害は少ない。

※OnErrorによるロジック制御を行っている場合はこの限りではないが、OnErrorをそのように使うのはおかしいので今回は考慮しない。

2) きちんとVBAのマニュアル(以下)で挙動が定義されているから。
https://msdn.microsoft.com/ja-jp/VBA/language-reference-vba/articles/select-case-statement

3) ついでにVBAの言語仕様書(リンク)でも定義されている。
※P82 Select Case Statement参照

Ifとの挙動の違いについて

Ifで複数条件のいずれかにマッチした場合を書く場合は、Or演算子を使用する。

If 条件1 Or 条件2 Then
    Debug.Print "いずれかにマッチしました。"
End If

ただしOr演算子は短絡評価されないので、条件1にマッチしたとしても必ず条件2も評価される。
AndやOrは一般的にIf文の機能だと思われているけれど、たまたまIfと一緒に使うことが多いだけでIfの機能ではない。
従って、IfとSelectで評価プロセスが異なるのではなく、Ifの後には式を一つしか書けず、Caseの後にはカンマ区切りで複数の式を書けるという違いだと思う。

IfThen
End If

Select CaseCase 式[, 式[, 式[,...]]]
End Select

ちなみにVB.Netには短絡評価される論理演算子AndAlsoとOrElseがある。この点はやっぱVBAレガシィ

VBA オブジェクトのメソッドチェーン

メソッドチェーンとは

メソッドチェーンとは、ひとつのオブジェクトに対して複数メソッドを1ステートメントで実行するテクニックである。
具体的には、オブジェクトのメソッドが自分自身を返すように設計することで、メソッドをドットで繋ぐだけで次々とそのオブジェクトに指令を与えることができるというもの。

例えばPersonというオブジェクトにNameとAgeの2つのフィールドとIntroduceという自己紹介をするメソッドがあるとする。

普通はこのように複数行で記述する。

p = New Person
p.Name = "Yamada Taro"
p.Age = 20
p.Introduce

メソッドチェーンテクニックを用いると、次のように記述することができる。

p = New Person
p.SetName("Yamada Taro").SetAge(20).Introduce

もちろん、デフォルトでこのような書き方ができるわけではなく、クラスモジュールをメソッドチェーンが可能なように設計しないといけない。

作り方

クラスモジュールPersonを用意し、次のコードを張り付ける。

Public Name As String
Public Age As Integer

Function SetName(name_ As String) As Person
    Name = name_
    Set SetName = Me
End Function

Function SetAge(age_ As Integer) As Person
    Age = age_
    Set SetAge = Me
End Function

Function Introduce() As Person
    Debug.Print "Hello!"
    Debug.Print "My name is " & Name & ". "
    Debug.Print "I'm " & Age & " years old."
    Debug.Print "Nice to meet you."
    Set Introduce = Me
End Function

メソッドチェーンはPropertyプロシージャとは相性が悪いのでFunctionで作っている。

あとは標準モジュールで次のように実行する。

Sub hoge()
    Dim p As Person
    Set p = New Person
    p.SetName("Taro Yamada").SetAge(20).Introduce
End Sub

実行結果がこちら。

Hello!
My name is Taro Yamada. 
I'm 20 years old.
Nice to meet you.

今回のPersonクラスではIntroduceも自分自身を返すように設計したので、次のように連続で人物情報を入れ替えることも。。

Sub hoge()
    Dim p As Person
    Set p = New Person
    p.SetName("Taro Yamada").SetAge(20).Introduce _
        .SetName("Jiro Suzuki").SetAge(30).Introduce _
        .SetName("Saburo Satou").SetAge(40).Introduce
End Sub

注意点として、今回は説明のためのサンプルであって、実務で利用する際は人物情報を表すオブジェクトを使いまわすのには問題がある。
たとえば年齢の変更忘れにより前の人物情報の一部が残ってしまうなど。
ブランクであれば出力結果を見ておかしいと気付けるけど、間違った情報が混入すると気付くきっかけが失われ、ビジネス上の問題に発展する。
安全で良いコードとは、プログラムのトラブルをプログラムの問題にとどめ、ビジネスの問題に発展させないコードである。

話が逸れたけど、メソッドチェーンの解説は以上。
VBAにはWithステートメントがあるのであんまりありがたみは無いかもしれないけど、将来「まさにここはメソッドチェーンの独断場!」と思う場面に出くわすかもしれないので一応紹介。

参考(英語)
www.sitepoint.com

このブログを通じて私がしたかったこと、していること。

このブログを書き始めた当時、VBA界隈は旧態依然としたコードで溢れていた。
全ての変数はプロシージャの先頭で宣言され、変数名は極端に省略されて読んでも意味が分からず、それを補完するために本来必要のないコメントでコードは散らかっていた。

プログラミング手法はトップエンジニア達の研究・議論・試行錯誤によって磨かれ、時代とともに進化している。
しかしExcel VBA使いの中には「ExcelにはExcelの流儀がある」と言って、新しい考えを受け入れない人もいる。私は当時、Excelの権威とされる人ですら、プログラミングという大きな流れを無視し、VBAをこれまでの小さな世界に閉じ込めようとしているように見えた。

ひょっとしたらVBA書きがプロのエンジニアから小馬鹿にされるのも、プログラミングという大きなフィールドで先人達が積み上げてきたものを無視して、独自の理論を展開するExcelの権威が幅を利かせているからではと思える。

私は、ノンプログラマーが自分の仕事を遂行するのに十分だと判断した結果、初級レベルに留まることは悪い判断だとは思わない。
しかしVBAの場合は少し状況が異なる。上のフロアが存在し、行きたい人間がいるのに階段が無い。そういう状況に思えた。

記事を重ねるうち、私のブログには一つの方針ができた。
それは、VBAレガシィをぶち壊す!こと。

別の言い方をすれば、鎖国政策をつづけてきたVBA界隈に黒船を送り込み、開国に導くことだ。(ちと大げさか)

これを実現するために具体的に私がしていることは、他言語やプログラミングの技術書で学んだことをVBA界隈に持ち帰って紹介すること。ただそれだけ。実のところ私自身の実力は大したことない。この世界の本当に凄いトップエンジニア達を思うと、自分の平凡さに溜息が出ることもしばしば。

それでも情報発信しつづけてよかったのは、最近少しずつVBA界隈が変わり始めたように思えることだ。このブログがVBA界隈に影響を与えた為かどうかは定かではない。しかしリーダブルコード等の良書がVBA界隈でも紹介されるようになり、最近目にするコードは比較的新しい考えを取り入れたものが増えている。

特に最近発売されたこの本はとても良い。より良い変数名の付け方から、クラスモジュールまで、私がレガシィ脱却のために重要だと思うところを完璧にカバーしている。

以前はVBAでこのレベルの情報はネットにしかなかった。
今後もこのような良書が沢山発刊され、いつの日かVBAレガシィが一掃されることを望む。

VBA マクロで利用する設定値をシートに保存し、文字列でアクセスするテクニック

今回はマクロで利用する設定値をシートに保存し、汎用的に文字列でアクセスできるようにするテクニックの紹介。

作り方

標準モジュールを挿入し、プロパティウィンドウからオブジェクト名をConfgに変更する。
f:id:t-hom:20180616031700p:plain

Configモジュールに以下のコードを張り付ける。

Option Explicit
Private configurations As Variant   '<- configulations is keepd here as variant array.

Private Property Get InitialSettings() As Collection
    Set InitialSettings = New Collection
    InitialSettings.Add Array("Name", "Value", "Description")   '←見出しです。
    
    '以下のサンプルに従って設定を定義してください。
    '書式は InitialSettings.Add Array([設定名] As String, [値] As Variant, [説明] As String) です。
    'ここに示したサンプルは削除して構いません。
    InitialSettings.Add Array("BackgroundColor", rgbWheat, "rgbWheat")
    InitialSettings.Add Array("Margin", 5, "how many blank cells put between pictures")
    InitialSettings.Add Array("InsertTime", True, "Write scraptime or not")
    InitialSettings.Add Array("StartRow", 5, "")
    InitialSettings.Add Array("StartColumn", 3, "")
End Property

Sub LoadConfig(Optional ByRef void = Empty)
    If Not existConfigSheet Then ResetConfig
    configurations = ThisWorkbook.Worksheets("Config").Range("a1").CurrentRegion.Value
End Sub

Public Sub ShowConfig()
    If Not existConfigSheet Then ResetConfig
    With ThisWorkbook.Worksheets("Config")
        .Visible = xlSheetVisible
        .Activate
    End With
End Sub

Public Sub HideConfig()
    If Not existConfigSheet Then ResetConfig
    ThisWorkbook.Worksheets("Config").Visible = xlSheetVeryHidden
End Sub

Public Property Get Value(conf_name As String)
    Dim i As Long
    For i = LBound(configurations, 1) To UBound(configurations, 1)
        If UCase(conf_name) = UCase(configurations(i, 1)) Then
            Value = configurations(i, 2)
        End If
    Next
End Property

Public Function LetValue(conf_name As String, conf_value As Variant) As Boolean
    Dim sh As Worksheet: Set sh = ThisWorkbook.Worksheets("Config")
    Dim i As Long
    For i = 1 To sh.Range("A" & Rows.Count).End(xlUp).Row
        With sh.Range("A" & i)
            If UCase(.Value) = UCase(conf_name) Then
                .Offset(0, 1).Value = conf_value
                LetValue = True
                Exit Function
            End If
        End With
    Next
End Function

Public Sub ResetConfig()
    If existConfigSheet Then
        With ThisWorkbook.Sheets("Config")
            Application.DisplayAlerts = False
            .Visible = xlSheetHidden
            .Delete
            Application.DisplayAlerts = True
        End With
    End If
    
    Dim configSheet As Worksheet
    With ActiveSheet
        Set configSheet = ThisWorkbook.Worksheets.Add(Sheets(1))
        .Activate
    End With
    configSheet.Name = "Config"
    Dim i, j
    Dim c As Collection: Set c = InitialSettings
    For i = 1 To c.Count
        Dim arr: arr = c(i)
        For j = LBound(arr) To UBound(arr)
            configSheet.Cells(i, j + 1).Value = arr(j)
        Next
    Next
    configSheet.Cells.EntireColumn.AutoFit
End Sub

Private Function existConfigSheet() As Boolean
    Dim ret As Boolean
    Dim sh As Object
    For Each sh In ThisWorkbook.Sheets
        If UCase(sh.Name) = UCase("Config") Then
            existConfigSheet = True
            Exit Function
        End If
    Next
End Function

ResetConfigマクロをF5で実行すると、Configシートが作成され、InitialSettingsプロパティで定義した内容が転記される。
f:id:t-hom:20180616031204p:plain

使い方

別の標準モジュールを挿入し、次のコードを張り付けて実行すると、アクティブシートの背景色がConfigシートで定義されたBackgroundColorの値になる。

Sub hoge()
    Call Config.LoadConfig
    ActiveSheet.Cells.Interior.Color = Config.Value("BackgroundColor")
End Sub

ResetConfigは設定をめちゃくちゃにしてしまった時のための初期化用マクロなので普段は実行せずに、手でシートを書き換えるか、マクロからConfig.LetValueを呼び出して値を書き換える。

カスタマイズ方法

InitialSettingsプロパティで指定されている5つのサンプルと同じように設定名、値、説明を定義する。
サンプルは消してOK。
ResetConfigを実行するとConfigシートが再作成される。

一度LoadConfigを読んでおけば、以降はConfig.Value([設定名])とすることで設定を参照できる。

説明

Config.LoadConfigを呼び出すと、Configシートの内容が2次元配列としてConfigモジュールに保持される。
Cellへのアクセスは低速なのでこのように配列に保持する仕組みにしている。

Config.Value([設定名])を呼び出すと、その設定名に応じた値を配列から検索して返す。

先日プロシージャの呼び出し順について言及したが、InitialSettingsはResetConfigから呼び出されるにも関わらずあえて先頭に置いた。
これはInitialSettingsがプロシージャの形をとっているものの実質は定数定義の代替手段に過ぎないためだ。
仕様が許すならGeneral領域に置きたいけどプロシージャである以上そうもいかないので呼び出しの原則を破って先頭に置いた。

Configをシートモジュールに統合してオブジェクト名でアクセスするというアイデアを採用すればもっとシンプルに出来たけれど、今回その手段を取らなかったのは人に配布するマクロではConfigシートそのものが誤って消されてしまう可能性があり、オブジェクト名によるアクセスが破綻する危険があるため。

標準モジュールだって消されるだろうって?
それはマクロに対する直接的な破壊行為なので、そもそもプログラムで対処すべき問題領域ではない。

以上

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