t-hom’s diary

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

VBA プロシージャ呼び出しの仕組みから再帰を理解する

以前に以下の記事で再帰を説明するのに、普通の関数呼び出しに置き換えるアプローチをとった。
thom.hateblo.jp

今回はそもそものプロシージャ呼び出しの仕組みから説明しようと思う。

例えば以下のコードでProcedureCall1を実行してみる。

Sub ProcedureCall1()
    a = 1
    Debug.Print a
    Call Proc1
    Debug.Print a
End Sub

Sub Proc1()
    a = 2
    Debug.Print a
End Sub

結果は1, 2, 1と表示される。
ProcedureCall1とProc1における変数aは、名前は同じだけれど別物として扱われる。これは経験を積んだプログラマからすると自明のことだ。

しかしなぜ一つのプログラム実行の中で、同じ変数を別物として扱えるのだろうか。
これはプロシージャ呼び出しに、スタックが使われてることと関係がある。

スタックとは一般的には「積み重ね」という意味で、プログラミング用語としては積み重ね構造を模倣したデータ構造を言う。
Pushという操作でデータを積み、Popという操作でデータを取り出すことができる。
f:id:t-hom:20180731035029p:plain

プロシージャを呼び出すと、以下の情報がスタックに積まれる。

  • 呼び出し元のローカル変数
  • 呼び出しが終わったらどこに戻れば良いのか(つまり呼び出し元情報)

今回の例では、ProcedureCall1がProc1を呼び出すとき、ローカル変数aと戻り先情報(ProcedureCall1の4行目に戻れ)がスタックに積まれる。
そしてProc1が終了すると、スタックから戻り先情報を取り出してその位置にジャンプし、ローカル変数aの中身を元に戻す。

ProcedureCall1のaの内容はスタックに保管されているのでProc1でaが加工されても全く影響がない。
これが基本的なプロシージャ呼び出しの仕組み。

ではVBAのGoSub命令を使ってこのプロシージャ呼び出しの仕組みを模倣してみようと思う。
GoSub命令はラベルにジャンプすることができ、Return命令によって元の位置に戻ることができる。
といっても戻り先情報については変数に格納したりできないので、ローカル変数のスタック処理だけやってみる。

まずVBAにスタックは無いので、自分で作るしかない。
クラスモジュールを挿入し、オブジェクト名を「StackObject」とする。

StackObjectのコードは以下のとおり。今回の記事では特にスタックの実装まで理解する必要はないのでコピペでOK。

Private Items() As Variant

Private Sub Class_Initialize()
    ReDim Items(0)
End Sub

Public Sub Push(ByRef x As Variant)
    ReDim Preserve Items(UBound(Items) + 1)
    Items(UBound(Items)) = x
End Sub

Public Function Pop() As Variant
    If UBound(Items) > 0 Then
        Pop = Items(UBound(Items))
        ReDim Preserve Items(UBound(Items) - 1)
    Else
        Pop = Empty
    End If
End Function

これを使ったプロシージャ呼び出し時のローカル変数スタックを模倣したコードがこちら。

Sub ProcedureCallImitation()
    Dim stack As StackObject: Set stack = New StackObject
    a = 1
    Debug.Print a
    
    'ローカル変数aをスタックに退避し、クリアする
    stack.Push a: a = Empty
    
    'プロシージャを呼び出す。
    GoSub Proc1
    
    '呼び出しから戻ったらスタックからローカル変数aに戻す
    a = stack.Pop
    
    Debug.Print a
Exit Sub

Proc1:
    a = 2
    Debug.Print a
    Return
End Sub

上記コードでは実際にはひとつのプロシージャの中で変数aを使いまわしているが、スタックを利用しているのであたかも別プロシージャの別の変数であるかのように扱うことができる。

普通のプロシージャの呼び出しでも、マシン語レベルではこれと同じことが起きている。ただプログラマーがスタックを意識しなくて良いようにVBAを含めた高級言語ではそのあたりが抽象化されているだけ。

次に同じ仕組みでプロシージャの再帰呼び出しをスタックを使ってやってみよう。
まずは普通の再帰コード。

Sub RecursiveProcedureCall()
    Dim arg As Integer
    arg = 1
    Call RecProc1(arg)
End Sub

Sub RecProc1(arg)
    If arg < 5 Then
        Call RecProc1(arg + 1)
    End If
    Debug.Print arg
End Sub

実行すると5, 4, 3, 2, 1と表示される。
処理の流れが分かりにくい場合はF8で一行ずつステップ実行すると良い。

これをスタックとGoSubを使って1つのプロシージャで書いたのがこちら。

Sub RecursiveProcedureCallImitation()
    Dim stack As StackObject: Set stack = New StackObject
    Dim arg As Integer
    arg = 1
    stack.Push arg
    GoSub RecProc1
Exit Sub

RecProc1:
    If arg < 5 Then
        arg = arg + 1
        stack.Push arg
        GoSub RecProc1
    End If
    arg = stack.Pop
    Debug.Print arg
    Return
End Sub

RecProc1を呼び出しす度にスタックには1, 2, 3, 4 ,5の順でデータがPushされる。
そして呼び出しの最後でスタックからPopされるので出力順は5, 4, 3, 2, 1となる。

このコードは同じ個所を繰り返し呼び出しているが、変数argは都度スタックにPushされているので、あたかもargは呼び出しごとに別のスコープであるかのように扱うことができる。

これも分かりにくければステップ実行してみると良いが、F8だとStackの内部コードにまで入り込んでしまうので、Shift+F8で1行ずつ実行すると良い。
Stack内部に入ってしまった場合は抜けるまでF8を何度か押すか、Ctrl+Shift+F8で抜けられる。

ちなみにGoSubでラベルにジャンプした場合にReturnで呼び出し元に戻ってこられるのも裏でスタックが働いているおかげ。ラベルを変数に入れられたらもっと細かいところまで模倣できたんだけど残念ながらVBAでは無理なので、イメージで補って欲しい。

まとめ

再帰は自分自身を呼び出すと聞いて、あれ?途中の変数はどうなるの?と混乱される方が多い。
かくいう私もそうだった。

プロシージャごとにローカルスコープが作られるという理解だと、この混乱を避けることができない。
正しくは、プロシージャの呼び出しごとにスタックによってスコープが作られるのだ。
だからたとえ呼び出し先が自分自身であってもスコープは新しく作られ、呼び出しから戻ればまた元のスコープに戻って、変数の値も元通りという訳だ。
これが再帰の仕組み。

余談

今回スタックというデータ構造を紹介したが、他にも色々と使い道があるので紹介。

ネストした構造を扱うのにスタックを使う。
thom.hateblo.jp
thom.hateblo.jp

普通は再帰で実装するアルゴリズムをあえてループで実装するにはスタックが必要。
thom.hateblo.jp

あとは記事は書いてないけれど、Undo-Redoの仕組みもスタックだし、ブラウザの戻る-進むもスタックである。

スタックは情報処理分野ではLIFO(Last In First Out)あるいはFILO(First In Last Out)と呼ばれるデータ構造だ。
今後もし直接使うことはなくても、思考の幅を広げてくれる面白いデータ構造なので、覚えておいて損はないと思う。

VBA 固定長のコードを扱うオブジェクトをクラスモジュールで作ってみた

業務では固定長のコードを扱うプログラムを作ることがある。

例えば以下のようなコードがあったとする。
20180728MPG

これは私が考えた適当なコードだが、2018年7月28日生まれの男性プログラマーを表すことにする。

つまり先頭8桁が生年月日、次の1桁が性別、最後の2桁が職業を表す。

たとえばこのコードの性別だけ取り出したいと思ったら、9文字目から1文字切り出すという処理が必要になるが、これが結構面倒くさいのでクラスモジュールを使って楽に取り出せる仕組みを考えてみた。

まずクラスモジュールを挿入し、オブジェクト名を「FixedLengthCode」に変更する。
クラスのコードはこちら。

Public Value As String
Private codeDefinisions() As codeDefinision
Private Type codeDefinision
    description As String
    startLocation As Integer
    length As Integer
End Type

Sub AddDefinition(description As String, length)
    ReDim Preserve codeDefinisions(UBound(codeDefinisions) + 1)
    codeDefinisions(UBound(codeDefinisions)).description = description
    codeDefinisions(UBound(codeDefinisions)).length = length
    codeDefinisions(UBound(codeDefinisions)).startLocation _
     = codeDefinisions(UBound(codeDefinisions) - 1).startLocation _
     + codeDefinisions(UBound(codeDefinisions) - 1).length
End Sub

Private Function getDefinision(description As String) As codeDefinision
    Dim i As Integer
    For i = LBound(codeDefinisions) To UBound(codeDefinisions)
        If codeDefinisions(i).description = description Then
            getDefinision = codeDefinisions(i)
            Exit Function
        End If
    Next
End Function

Sub ReplacePart(description As String, code As String)
    Dim d As codeDefinision
    d = getDefinision(description)
    Mid(Value, d.startLocation, d.length) = code
End Sub

Function GetPart(description As String) As String
    Dim d As codeDefinision
    d = getDefinision(description)
    GetPart = Mid(Value, d.startLocation, d.length)
End Function

Private Sub Class_Initialize()
    ReDim codeDefinisions(0)
    codeDefinisions(0).description = "dummy"
    codeDefinisions(0).length = 0
    codeDefinisions(0).startLocation = 1
End Sub

このFixedLengthCodeオブジェクトを使うと、コードのパーツごとに名前をつけて名前でアクセスできるようになる。
そしてこのオブジェクトを使って固定長コードを扱うサンプルはこちら。

Sub Main()
    'オブジェクト生成
    Dim code As FixedLengthCode
    Set code = New FixedLengthCode
    
    '定義の登録。定義名と桁数を順に登録する。
    code.AddDefinition "生年月日", 8
    code.AddDefinition "性別", 1
    code.AddDefinition "職業", 2
    
    '値のセット
    code.Value = "20180728MPG"
    
    'パーツの取り出しサンプル
    Debug.Print code.GetPart("職業")
    Debug.Print code.GetPart("生年月日")
    Debug.Print code.GetPart("性別")
    
    'パーツの置換サンプル
    code.ReplacePart "性別", "F"
    Debug.Print code.Value
    
    code.ReplacePart "生年月日", "20170509"
    Debug.Print code.Value
    
    code.ReplacePart "職業", "SE"
    Debug.Print code.Value

End Sub

以上

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

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