VBA で small is beautiful. する話【修正版】
※前の記事はコードの色表示が上手くいかなかったので削除しました。
( ´_ゝ`)ノシ
yoshitiaです。
前の現場ではbashとSQLでしたが、
新しい現場では VBA 、 VBscript 、コマンドプロンプトとMSづくしです。
そんな現場ですが Unix哲学が役にたたないわけではないです。
今回の記事では Unix哲学の1番目に出てくる
small is beautiful. (小さいことはいいことだ)
コードの単位を細かく細かくすることで
扱いやすいプログラムにする考え方です。
これをVBAで実践する例を書いていこうと思います。
https://www.amazon.co.jp/dp/4274064069/ref=cm_sw_r_tw_dp_x_M983xbVA8A8SXwww.amazon.co.jp
環境
Excel2007以降のxlsmファイルに標準モジュール作成
標準モジュール上でコードを書く。
small is beautiful. 実践前のコード
Option Explicit Sub ワークシートチェック() Dim ワークシート As Worksheet Dim セルからの取得値 As String Set ワークシート = ThisWorkbook.Worksheets("sheet1") セルからの取得値 = ワークシート.Range("A1").Value If セルからの取得値 = "" Then MsgBox "A1セルが空です" Else MsgBox "A1セル空欄チェックOK" End If End Sub
sheet1 の A1セルの値が空かどうかチェックする簡単なコードです。
例としてはまだ処理が少ないので読めますが
後から読む人にとっては何の処理をしているか
すぐにはつかみにくいです。
これを書き換えてみます。
small is beautiful. 実践後のコード
Option Explicit Sub ワークシートチェック() Dim ワークシート As Worksheet Dim セルからの取得値1 As String Set ワークシート = ThisWorkbook.Worksheets("sheet1") セルからの取得値1 = ワークシート.Range("A1").Value MsgBox セルチェック1("A1", セルからの取得値1) End Sub Function セルチェック1(セルの場所 As String, セルからの取得値 As String) As String セルチェック = 空欄チェック(セルの場所, セルからの取得値) End Function Function 空欄チェック(セルの場所 As String, セルからの取得値 As String) As String If セルからの取得値 = "" Then 空欄チェック = セルの場所 & " が空です" Exit Function End If 空欄チェック = セルの場所 & " 空欄チェックOK" End Function
実践後のコードです。
人によっては「余計に複雑になった」と思う人もいるかもしれません。
ここで、メインの ワークシートチェック() だけを見てみます。
Option Explicit Sub ワークシートチェック() Dim ワークシート As Worksheet Dim セルからの取得値1 As String Set ワークシート = ThisWorkbook.Worksheets("sheet1") セルからの取得値1 = ワークシート.Range("A1").Value MsgBox セルチェック1("A1", セルからの取得値1) End Sub
ここからさらに見る範囲を絞ります。
Set ワークシート = ThisWorkbook.Worksheets("sheet1") セルからの取得値1 = ワークシート.Range("A1").Value MsgBox セルチェック1("A1", セルからの取得値1)
Excelを扱ったことのある人なら
「sheet1 の A1セルに入力されている値をチェックしている」
となんとなくは読み取れると思います。
では、最初のコードではどうでしょう。
Option Explicit Sub ワークシートチェック() Dim ワークシート As Worksheet Dim セルからの取得値 As String Set ワークシート = ThisWorkbook.Worksheets("sheet1") セルからの取得値 = ワークシート.Range("A1").Value If セルからの取得値 = "" Then MsgBox "A1セルが空です" Else MsgBox "A1セル空欄チェックOK" End If End Sub
このコードだと If文以降を全部読んで確認しないと
A1セルをチェックしている処理だと把握できません。
コードを小さくする時の考え方
コードの何を小さくしているかの話です。
具体的には処理の内容を分割しています。
コード全体の処理内容は
「sheet1 の A1セルの値が空かどうかチェックする」です。
これを次のような単位に分けます。
- 「セルの値をチェック」
- 「セルが空かどうかチェック」
「セルが空かどうかチェック」の部分は
「セルの値をチェック」の中に入っています。
利用する人は、「セルの値をチェック」に
対象のセルの値をセットするだけで使えます。
利点その1. 再利用しやすい
コピペで使いやすいです。
例えば、追加で「A2セルもA1セルと同じチェックをしたい」場合、
変数宣言に```Dim セルからの取得値2 As String```を追加
値取得に```セルからの取得値2 = ワークシート.Range("A2").Value```を追加
ここまでは実践前・実践後両方同じですが
処理を追加する場合、
small is beautiful. 実践前
If セルからの取得値2 = "" Then MsgBox "A2セルが空です" Else MsgBox "A2セル空欄チェックOK" End If
small is beautiful. 実践後
MsgBox セルチェック1("A2", セルからの取得値2)
追加する量が変わってきます。
チェックするセルの数が増えるほど差が出ます。
利点その2. 機能追加しやすい
ただ、これだけでは
Function セルチェック1 と Function 空欄チェック
この2つに分けた利点が分かりにくいと思います。
私も今までの説明だけなら
Function セルチェック1 に Function 空欄チェック のコードも
まとめればいいと思います。
しかし、このように分けているのも理由があります。
例えば、「A1セルの値が"1"かどうかのチェックも追加したい」
となった場合、Function セルチェック1にまとめてしまっていると
融通が効きません。
Function セルチェック1 と Function 空欄チェック のように分けていると
A1セルの処理
- 「セルの値をチェック」
- 「セルが空かどうかチェック」
- 「セルの値が1かどうかチェック」
A2セルの処理
- 「セルの値をチェック」
- 「セルが空かどうかチェック」
のように対象のセルごとにチェックの種類を
楽々追加/削除ができるようになります。
実際のコードは以下のとおりになります。
Option Explicit Sub ワークシートチェック() Dim ワークシート As Worksheet Dim セルからの取得値1 As String Dim セルからの取得値2 As String Set ワークシート = ThisWorkbook.Worksheets("sheet1") セルからの取得値1 = ワークシート.Range("A1").Value MsgBox セルチェック1("A1", セルからの取得値1) セルからの取得値2 = ワークシート.Range("A2").Value MsgBox セルチェック2("A2", セルからの取得値1) End Sub Function セルチェック1(セルの場所 As String, セルからの取得値 As String) As String Dim チェックする値 As String チェックする値 = "1" セルチェック = 空欄チェック(セルの場所, セルからの取得値) セルチェック = セルチェック & vbCrLf & 値チェック(セルの場所, セルからの取得値, チェックする値) End Function Function セルチェック2(セルの場所 As String, セルからの取得値 As String) As String セルチェック = 空欄チェック(セルの場所, セルからの取得値) End Function Function 空欄チェック(セルの場所 As String, セルからの取得値 As String) As String If セルからの取得値 = "" Then 空欄チェック = セルの場所 & " が空です。" Exit Function End If 空欄チェック = セルの場所 & " 空欄チェックOK。" End Function Function 値チェック(セルの場所 As String, セルからの取得値 As String, チェックする値 As String) As String If セルからの取得値 <> "1" Then 空欄チェック = セルの場所 & "の値が " & チェックする値 " ではありません。" Exit Function End If 値チェック = "チェックOK。 " & セルの場所 & " の値は " & セルからの取得値 & " です。" End Function
コードの量は増えましたが
ワークシートチェック()の部分だけを見てみると
Option Explicit Sub ワークシートチェック() Dim ワークシート As Worksheet Dim セルからの取得値1 As String Dim セルからの取得値2 As String Set ワークシート = ThisWorkbook.Worksheets("sheet1") セルからの取得値1 = ワークシート.Range("A1").Value MsgBox セルチェック1("A1", セルからの取得値1) セルからの取得値2 = ワークシート.Range("A2").Value MsgBox セルチェック2("A2", セルからの取得値1) End Sub
追加前と比較して
Dim セルからの取得値2 As String
セルからの取得値2 = ワークシート.Range("A2").Value
MsgBox セルチェック2("A2", セルからの取得値1)
の追加だけで済んでいます。
初めてこのコードを見る人でも
「sheet1 の A1セル と A2セル のチェック処理」
と概要を把握しやすいです。
まとめ
最後に、Unix哲学を元にコードを書いている時の私のイメージは
ゲームで1000回攻撃しないと倒せない相手を、
必殺技などを使わずに
愚直に連続攻撃を、隙間なく、1000回叩きこんで倒すイメージです。
(実際のゲームでは1000回は不要でしょうが)
https://www.amazon.co.jp/dp/4274064069/ref=cm_sw_r_tw_dp_x_M983xbVA8A8SXwww.amazon.co.jp
『UNIXという考え方』でもゴジラを題材に
Unix哲学を元に難問に立ち向かう開発者のイメージが語られています。
例として示したコードはまだまだ使い勝手が良くないです。
もっと良いコードの書き方はあるはずです。探しましょう。
Unix哲学についてはsmall is beautiful. 以外にもありますので
webの記事や書籍『UNIXという考え方』で見ると良いと思います。
vim-jp や emacs-jp にログインしてみる話
( ´_ゝ`)ノシ
yoshitia です。
asciidoctor 試してみたくてこの記事書いています。
markdownでもはてな記法でも多少手直ししてからでないと
駄目っぽいな・・・。
記事の内容自体は vim-jp や emacs-jp に参加する時の話です。
2017/09/22
Invite to vim-jp.slack.com
vim-jpのslackができたようです。
上記リンクにメールアドレス入力→招待メールのリンク先からアカウント登録できます。
SlackArchive.io
過去ログはここで見れます。
こちらはアカウント登録なしでログを確認できます。
目次
2. 最近?できたログイン先
2.1 vim-jp
2016年8月現在、 vim-jp は vimrc読書会(毎週土曜日、23時開催)を
gitterの vim読書会部屋 で行っています。
ここは、 twiter か githubアカウントでログイン可能です。
2.2 emacs-jp
emacs-jpは emacs-jp slack の利用を始めているようです。
こちらはリンク先から登録するメールアドレスを入力、
送られてきたメールから登録を行います。
Vimとサクラエディタでcsvに立ち向かう正規表現
( ´_ゝ`)ノシ
yoshitiaです。
夏になったのでトップの画像変えなきゃと思いつつ秋を迎えそうです。
タイトルの話ですが、DBとかでテスト用データ作る時、
csvを軽く編集したい、そういうことありますよね。
で、大量データは自動生成するとして
作った後に微妙に修正したい箇所が出来た時、
直接編集したい、そういうのありますよね?
でも投入するデータのcsvが1行数百カラムだったり、
複数の項目に入る文字数が均一でなくてレコードごとに
目的のカラムの位置が異なる。
(ノ`Д´)ノ彡┻━┻ としたくなりますが
お仕事だとそうはいかないわけで。
そこで検索のシンタックスハイライトを利用して
カラムの位置を特定して作業を楽にします。
サクラエディタの場合だと
検索で正規表現を使用するチェックを入れてから
^[^,]*,([^,]*,){2}
を検索窓に入力して実行します。
書いてる例の正規表現の意味は
^[^,]*,が行頭から一つ目のカンマまでの文字列を検索対象にしていて
([^,]*,){2}は()の中の検索対象の{n}回繰り返しを検索対象にしています。
例の場合はカンマ以外の文字列とその後ろに付くカンマのセットの2回繰り返しが検索対象です。
この2つを繋げると、
「行頭からcsvの3カラム目までを検索対象にする」正規表現になります。
{}の数字を増減させると検索対象になるカラム数を調節できます。
Vimの場合だと
\v^[^,]*,([^,]*,){2}
これを/の入力後に入力します。
頭に\vが付く以外はサクラエディタの場合と同じです。
Happy Vim Life!
Netezzaのメモ。
( ´_ゝ`)ノシ
最近記事書くのご無沙汰なyoshitiaです。
Netezzaについてちょっとだけメモを書く。
Oracleのlistagg関数、すごく便利ですね。
TEST table
NO | WORD
--------------
1 | Apple
2 | Ball
1 | Cat
1 | Dog
2 | Egg
---------------------------------------
SELECT
No
, LISTAGG('WORD',', ') AS RESULT
FROM
TEST
GROUP BY
NO;
---------------------------------------
NO | RESULT
-------------------
1 | Apple, Cat, Dog
2 | Ball, Egg
---------------------------------------------------
netezzaだとgroup_concatという関数があるみたい。
(デフォルト関数ではない。導入方法は下記リンク)
ただし、マルチバイト文字非対応らしく、扱う値が日本語だと文字化けする。
コードはC++みたいだけどC++力が無いのでどう修正したらいいのかわからないのがつらい。
新しく使うなら日本語を扱わない様にデータ用意するしかないんだろうなぁ・・・。
Ubuntuでのfcitx+vim or emacsの日本語インライン入力の話。
( ´_ゝ`)ノシ
yoshitiaです。
使いたいソフトの関係でLinuxPCのOSを
Arch LinuxからUbuntu Server 14.04に変更しました。
vim: version 7.4.1747
emacs: version 24.5.1
Linuxだと毎回日本語周りで面倒が起きるわけで。
今回も色々あったので対処した記録をここに残します。
インライン入力って何よ?
PCで日本語入力を行う時、
EnterやC-mで確定させる前に
下線と一緒に入力した文字が表示されているアレです。
Linuxでは上手く設定されていないと
Enterで確定させるか変換の為にスペース押したりしないと
「何を入力しているのか見えない」
つらい。
この記事はそれについての話。
fcitx+gvimでの日本語インライン入力
このあたりが原因らしい。
github.com
Ubuntuだと /usr/share/applecations あたりにGVim(gvim.desktop)があるので
プロパティから
gvim -f %F
を
vim -gf %F
に修正するとインライン入力が表示される。
fcitx+emacs(gui)での日本語インライン入力
この記事のやり方でいけそう。
mochalog.hatenablog.com
最後に
流石に何回もやってると対処法探すまでの時間が短くなってて草。
何故fcitxだけか?親指シフト使いたいからですハイ。
VimやEmacsのコマンド入力とかの話。
( ´_ゝ`)ノシ
yoshitiaです。
backspace.fmのVim-side#002を聞いて久しぶりにスパルタンVimを読み返しました。
backspace.fm
www.kaoriya.net
pdfによると、キー配置を変えずにコマンドを増やせ、とありますが
コマンド打つのが面倒だと思うのが普通だと思います。
が、Vimのコマンド補完地味に優秀だったりする・・・。
Vimのコマンド補完
組み込みの関数は大抵省略形があります。(help見よう)
:w[rite] :q[uit] :e[dit] :vim[grep]
AFunctionという名前のコマンドがあった場合、
:AFu
くらいまで打ってタブを押すと残りを補完してくれる。
:AFunction
複数候補がある場合ステータスラインに候補を表示してくれる。
|AFunction1 AFunction2| :AFunction
試しに:aと打ってからタブを押すといくつか表示されるはずです。
複数ある場合は
(この機能はコマンド名の大文字小文字は気にしません)
もし、何文字か入力して前方一致するコマンドが1つしかない場合は
タブで補完しなくても指定のコマンドを実行してくれます。
yoshitiaはtwit.vimを使っていてホームのタイムラインを見るとき、
:FriendsTwitter
を使いますが、
yoshitiaのVim設定だとFrから始まるコマンドはこれ1つだけなので
:Fr
とタイプしてEnterrまたは<C-m>するだけで実行できます。
(この機能はコマンド名の大文字小文字をきっちり区別して入力しないと
上手く動いてくれません)
この機能を把握していればよく使うコマンドの最低必要タイプ数を
覚えているだけでかなり楽になります。
作業用の即席コマンド名の頭数文字をホームポジションの文字で作ると
すごく便利だと思います
実際プラグインの関数はa,s,d,f,gで始まるコマンドが多いようで
左手で少ないタイプ数からコマンドを実行して
右手でカーソル移動や検索での移動をしようという思想を感じます。
(※個人の感想です。)
Emacsのコマンド補完
この辺の話はるびきちさんの本か記事で見たと思います。
記憶が怪しい・・・。
るびきち「日刊Emacs」
EmacsではM-xからコマンドを入力しますが
コマンド名が
eval-last-sexp
のようになっていて
慣れていないと
M-x eval
と入力してタブを押して
候補を確認する、とやるのが普通ですが
ハイフン区切りごとの頭2文字ずつとハイフンで
入力文字数を省略できます。
yoshitiaのEmacs設定だとeval-last-sexpを
M-x ev-la
と入力した後Enterまたは<C-m>で実行できます。
これは最初の区切りがevで始まって
次の区切りでlaから始まるコマンドが
これしかないからです。
(個々人のEmacs設定では違うこともあると思います)
ただ、なかなか最初の区切りの2文字だけで
入力できるケースが少ない気がするので
Emacsで使用頻度の高いコマンドは素直に
キーバインドに設定しちゃった方がいい気がします。
スパゲティコードを見たので書いた。
( ´_ゝ`)ノシ
yoshitiaです。
ニコニコ動画でスパゲティコードなるネタが出てたので
それを書いた。
高画質版 とあるエンジニアのアイドルプロデュースログ 劇場版_3.Log ‐ ニコニコ動画:GINZA
#include<stdio.h> int main(void) { int month, day; char* m; printf("What month is it now? Type between 1 and 12.: "); scanf("%d", &month); // キーボード入力から10進数を取得 if (month == 1) { day = 31; m = "Jan";} else if (month == 2) { day = 28; m = "Feb";} else if (month == 3) { day = 31; m = "Mar";} else if (month == 4) { day = 30; m = "Apr";} else if (month == 5) { day = 31; m = "May";} else if (month == 6) { day = 30; m = "Jun";} else if (month == 7) { day = 31; m = "Jul";} else if (month == 8) { day = 31; m = "Aug";} else if (month == 9) { day = 30; m = "Sep";} else if (month == 10){ day = 31; m = "Oct";} else if (month == 11){ day = 30; m = "Nov";} else if (month == 12){ day = 31; m = "Dec";} else { day = -1; m = "error";} // 取得した値が1~12以外の数か、数字でない場合 if (m == "error") { printf("usage: Type number between 1 and 12\n");} // "使用法: 1から12までの数値を入力してください\n" else { printf("%s is %d days.\n", m, day);} // 出力例: Jan is 31 days. return 0; }
細かい所は違う気がするが気にしない。
( ゚Д゚)ウヒョーelseifが目に痛い。
Vimで書きましたまる
2016/3/12追記
プロログPより
else { day = -1; m = "error";}をelse { day = -1:}にして
if (m == "error")をif (day == -1)にするとシンプルになるよと
赤ペン先生してもらった。
( ゚д゚ )
( ゚д゚ )
( ゚д゚ )