プログラム作成時 Ctrl+C Ctrl+Vの前によく考えよう

[VBA系メニューへ] [質問掲示板] [バックナンバー目次]
こんにちは、三流プログラマーのKen3です。

三流君の書いてるメルマガ
http://www.ken3.org/guchi/    作者の貧しい心の中が見れる愚痴系
で、
自分の貧しい技術に対する愚痴で書いたんだけど、
愚痴系で発行後、読み直すと、
VBA系の読者に見せたほうが、よさそうだったので、
こっちでも発行してます。
※愚痴系、VBA系読んでて、重複してしまった人はスミマセン。

三流君のお馬鹿な開発話を笑ってください
※反面教師でマネしないでね。

--- ここから はじまりです ---

どちらかと言えば、芸術的プログラムとは無縁の三流プログラマーです。
えっ、そんなのわかってるって?

そんな前フリは置いといて、
今回は、
Ctrl+C + Ctrl+V を使う前に一呼吸置こうよ
コピーして編集するプログラムは工夫できる率が高いよ
って話です。

まぁ、いつもの独り言だけど、聞いてください。

/* * 1.今回のキッカケ */

VBA系のメルマガで、And演算子、Not演算子の解説を行ってました。 自己レスじゃないけど、フトお馬鹿なことに気がつく。 ※いつになったら、手前味噌的な自慢系のメルマガに成長できるのだろう(笑)  オレ様のプログラムってキレイだろ、芸術品だよ、、と  いつか自信を持って、言って見たいよね。。。 おっと、挨拶はこれくらいにして、

/* * 2.コピー -->貼り付け で作るプログラムの罠(笑) */

No.76 Access レポート Me.NextRecord = Falseで移動を止める http://www.ken3.org/backno/backno_vba16.html#76 左端ならラベルを表示、レコード移動を止め次に実データを印刷する そんなテストプログラムなのですが、 初めは下記のように、普通に作成してました。
Private Sub 詳細_Format(Cancel As Integer, FormatCount As Integer)
    If (Me![cntA] Mod 3) = 1 Then  '左端なら
        If pFLG = False Then 'まだラベルエリアを印刷してなかったら
            Me.NextRecord = False      'レコードの移動をまず止める
            pFLG = True  '印刷フラグを立てる
               'ここにラベルのOn/Off処理を入れる
            Me![lab01].Visible = True
            Me![lab02].Visible = True
               '実データをOff  <−−−注目(笑)
            Me![ID].Visible = False
            Me![WrietTime].Visible = False
            Me![F_TITLE].Visible = False
            Me![F_MEMO].Visible = False
        Else
               'ここにラベルのOn/Off処理を入れる
            Me![lab01].Visible = False
            Me![lab02].Visible = False
               '実データをOff  <−−−注目(笑)
            Me![ID].Visible = True
            Me![WrietTime].Visible = True
            Me![F_TITLE].Visible = True
            Me![F_MEMO].Visible = True
        End If
    Else
        pFLG = False  'その他の時
    End If

End Sub
よく見ると、同じような処理があって、 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 'ここにラベルのOn/Off処理を入れる Me![lab01].Visible = True と Else 'ここにラベルのOn/Off処理を入れる Me![lab01].Visible = False みたいな感じでOn/Offが逆なだけでした。 まぁ、処理が 左端ならラベルを表示、実データは非表示、 その他はラベル非表示、実データを表示だから、 True/Falseが違うだけで同じなんですよね。 そっか、それで、キーボードでCtrl+C,Ctrl+Vでコピーして、 True/Falseを逆に書いたんだ。 えっ、なんでCtrl+C,Ctrl+Vでコピーしたってわかったの? だってさ、コメントが直ってないんだよ、アンタのコメントが? えっ、どこどこ? よく見ろよ、 If pFLG = False Then 'まだラベルエリアを印刷してなかったら Me.NextRecord = False 'レコードの移動をまず止める pFLG = True '印刷フラグを立てる 'ここにラベルのOn/Off処理を入れる Me![lab01].Visible = True Me![lab02].Visible = True '実データをOff <−−−注目(笑) Me![ID].Visible = False Me![WrietTime].Visible = False Me![F_TITLE].Visible = False Me![F_MEMO].Visible = False と Else 'ここにラベルのOn/Off処理を入れる Me![lab01].Visible = False Me![lab02].Visible = False '実データをOff <−−−注目(笑) Me![ID].Visible = True Me![WrietTime].Visible = True Me![F_TITLE].Visible = True Me![F_MEMO].Visible = True End If を良く見ると、 '実データをOff ^^^^^^^^^^^^^^^^^ ってコメントが一緒だよ。 Elseの部分のコメントは、実データをon、可視にする ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ が正しいんじゃないの? コピーして作るのもいいけど、直してね、 コメントだから、まだ実害なかったけど、 もし、TrueとFalseを逆にするのを直し忘れてたら? ゾッとするよね。 最近の開発スタイルって、エディターに直打ち、即実行確認、 なんて環境だから、 一呼吸置いて、考えないでコピー、貼り付けって多いと思う。 で、コピーした一部(部分的範囲)を変更する。 修正中にメールや携帯が鳴って、割り込みが入り作業中断、 そしたら、修正忘れがあったり、、なんて変なストーリーあったりしてね。 たまたま、今回は、 VBA系の No.77 Not演算子で細工する、プログラムはパズルのように... http://www.ken3.org/backno/backno_vba16.html#77 で、 Not演算子を使用して下記のようにまとめてみたけど、 何も考えないと、私のように同じ処理が氾濫してたりするよ(笑) 手前味噌、芸術的、ナルシスト的プログラムって言われてもたまには作ってみようよ。
Private Sub 詳細_Format(Cancel As Integer, FormatCount As Integer)
    If (Me![cntA] Mod 3) = 1 Then  '左端なら
        'フラグを使用して可視/不可視をセットする
        Me![lab01].Visible = Not pFLG  'ラベルはフラグの逆をセット
        Me![lab02].Visible = Not pFLG
        '実データ
        Me![ID].Visible = pFLG         '実データにはフラグそのままセット
        Me![WrietTime].Visible = pFLG
        Me![F_TITLE].Visible = pFLG
        Me![F_MEMO].Visible = pFLG
        
        If pFLG = False Then 'まだラベルエリアを印刷してなかったら
            Me.NextRecord = False      'レコードの移動をまず止める
            pFLG = True  '印刷フラグを立てる
        End If
    Else
        pFLG = False  'その他の時
    End If

End Sub
と、Not演算子を使用して、まとめました。 これなら、項目の1つだけTrue/Falseの修正忘れの単純ミスもないし、 項目が増えても、2ヶ所に処理を入れなくてOKです。

/* * 3.プログラムには、ループや配列を使った処理が存在するんだよ(笑) */

画面に1回目、2回目、、4回目とMsgboxで表示してよと言われたら、 簡単なプログラムは、
Sub AAA()
   Msgbox "1回目"
End Sub
って書いてから、いつものCtrl+CでコピーCtrl+Vを3回押してと
Sub AAA()
   Msgbox "1回目"
   Msgbox "1回目"
   Msgbox "1回目"
   Msgbox "1回目"
End Sub
としてから、
Sub AAA()
   Msgbox "1回目"
   Msgbox "2回目"
   Msgbox "3回目"
   Msgbox "4回目"
End Sub
と直します(笑) そんなことしないよFor文使うよ ~~~~~~~~~~~~~~~~~~ って、みなさん、心の中で言ったと思います (私が勝手にアナタの心の中を予想しました) ですよね、まぁ、コピー、一部変更プログラム極論でした。 では、チエちゃん好き、ミキちゃん好き、アキちゃん好き、マイちゃん好き と名前を出すときは?
Sub AAA()
   Msgbox "チエちゃん好き"
   Msgbox "ミキちゃん好き"
   Msgbox "アキちゃん好き"
   Msgbox "マイちゃん好き"
End Sub
じゃなくって、
Sub BBB()

    Dim strNAME As Variant
    Dim n As Integer
    '配列を代入
    strNAME = Array("チエ", "ミキ", "アキ", "マイ")
    'データを表示
    For n = 0 To 3
        MsgBox strNAME(n) & "ちゃん好きです"
    Next n

End Sub
と、ループとArrayで配列を作成の知識のある人は作るかな。

/* * 4.配列の要素数を返すUBound関数を使ったりする */

表示させたい、好きな子が増えたら? コピー君は、
Sub AAA()
   Msgbox "チエちゃん好き"
   Msgbox "ミキちゃん好き"
   Msgbox "アキちゃん好き"
   Msgbox "マイちゃん好き"
   Msgbox "アヤちゃん好き"  '午後の紅茶のアヤちゃんは好きじゃないけど(笑)
End Sub
とコピーで作成するのかな。 For君は、
Sub BBB()
    Dim strNAME As Variant
    Dim n As Integer
    '配列を代入
    strNAME = Array("チエ", "ミキ", "アキ", "マイ", "アヤ")
    'データを表示
    For n = 0 To 4 'カウンタも忘れずに増やす
        MsgBox strNAME(n) & "ちゃん好きです"
    Next n
End Sub
とデータとカウンタを増やすのかな。 まぁ、忘れそうなのが、データ増やしてカウンタを増やすことを忘れるパターン。 それを防止する方法?があって、 昔からの古典的手法は データの最後に"END""STOP","99"など、 ストップワードを入れて判断する方法もある。
Sub CCC()
    Dim strNAME As Variant
    Dim n As Integer
    '配列を代入
    strNAME = Array("チエ", "ミキ", "アキ", "マイ", "アヤ", "STOP")
    'stopまでデータを表示
    n = 0 'カウンタ初期化
    While strNAME(n) <> "STOP"  'STOP以外の間ループする
        MsgBox strNAME(n) & "ちゃん好きです"
        n = n + 1  'カウントアップ次の子にする
    Wend
End Sub
これだと、ループの回数は指定しないで、"STOP"までループ可能。 まぁ、今風の書き方だと、配列の最大要素数を求めるUBoundって関数があって、
Sub DDD()
    Dim strNAME As Variant
    Dim n As Integer
    '配列を代入
    strNAME = Array("チエ", "ミキ", "アキ", "マイ", "アヤ")
    'データを表示
    For n = 0 To UBound(strNAME) '配列の最大要素までループ
        MsgBox strNAME(n) & "ちゃん好きです"
    Next n
End Sub
ASPで作った例だけど、Split関数とUBound関数のサンプル http://www.ken3.org/cgi-bin/test/test024-2.asp こっちも参考にしてみてね。

/* * 5.コントロールをループさせる For Each IN を使って */

この流れの解説だと、(話の流れだと) 読者の心の声をまたまた勝手に代弁すると、 ^^^^^^^^^^^^^^ データをプログラム内に持たないで、ファイルなど外部に持つ方法だろ、、 と 普通は来るのですが、疲れたので、今日は辞めます(笑) オイオイ、そりゃないでしょ。 えっと、このメルマガのタイトルに戻って、 タイトル?なんだっけ? 三流君、手前味噌的にプログラムを語る?だったっけ・・・ 違うでしょ、 Ctrl+C + Ctrl+V を使う前に一呼吸置こうよ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ってタイトルです。 下記のプログラムに戻ってと、 実は、ここでもCtrl+C Ctrl+Vでプログラム書いてるんですよね。 Me![ID].Visible = pFLG をコピーして、 Me![F_TITLE].Visible = pFLG Me![F_MEMO].Visible = pFLG なんて、作ってるんですね。 ※フィールド名が違うだけなので、コピーしてフィールド名を修正するかな普通。
Private Sub 詳細_Format(Cancel As Integer, FormatCount As Integer)
    If (Me![cntA] Mod 3) = 1 Then  '左端なら
        'フラグを使用して可視/不可視をセットする
        Me![lab01].Visible = Not pFLG  'ラベルはフラグの逆をセット
        Me![lab02].Visible = Not pFLG
        '実データ
        Me![ID].Visible = pFLG         '実データにはフラグそのままセット
        Me![WrietTime].Visible = pFLG
        Me![F_TITLE].Visible = pFLG
        Me![F_MEMO].Visible = pFLG
        
        If pFLG = False Then 'まだラベルエリアを印刷してなかったら
            Me.NextRecord = False      'レコードの移動をまず止める
            pFLG = True  '印刷フラグを立てる
        End If
    Else
        pFLG = False  'その他の時
    End If

End Sub
このプログラムでもいいんだけど、 実データの印刷フィールドが追加になったら、(F_TAKOフィールド) Me![F_TAKO].Visible = pFLG と、プログラムの追加が発生します。 ※これも、上の一行Me![F_MEMO].Visible = pFLGをコピー後に名称変更かな フィールドが追加になったから、ショウガナイヨネ、これは。 まぁ、そうなんですが、よく見ると、 Me![lab01]とMe![lab02]のラベル関係 と 実データのMe![F_XXXXX]の実データ関係 そんな2つのグループ分けができてます(IDとwriteなど例外あるけど(笑)) コントロールの名前付けで、 規則性があるなら、ループでまわせよってことで、 ↑~~~~~~~~~~~~~~~~(コントロール名を規則正しく作ってね) Controlsコレクションでまわしてみます。 Controls?コレクション? コントロールにアクセスする方法は、 Me.Controls!新規データ Me.Controls![新規データ] Me.Controls("新規データ") なんて、名前を指定する方法と、 インデックスでコントロールを参照することもできます。 Me(0) ' コレクションの最初の項目を参照します。 Me.Controls(0) これを利用して、
Private Sub 詳細_Format(Cancel As Integer, FormatCount As Integer)
    Dim n As Integer
    If (Me![cntA] Mod 3) = 1 Then  '左端なら
        For n = 0 To Me.Controls.Count - 1 'コントロールの数ループする
            If Left(Me.Controls(n).Name, 3) = "lab" Then '名前はラベル
                Me.Controls(n).Visible = Not pFLG
            Else
                Me.Controls(n).Visible = pFLG
            End If
        Next n
        If pFLG = False Then 'まだラベルエリアを印刷してなかったら
            Me.NextRecord = False      'レコードの移動をまず止める
            pFLG = True  '印刷フラグを立てる
        End If
    Else
        pFLG = False  'その他の時
    End If
End Sub
と書いて、 コントロールの名前付けの規則でTrue/False を 切り替える方法も1つの手です。 Me.Controls(n).Visible あっ、これが三流君のうわさのクセね。 ループのカウンタでまわしたがるってヤツね。 VBA系の No.73 オブジェクトのループはFor Each In でループさせる http://www.ken3.org/backno/backno_vba15.html#73 で、 偉そうなこと語ってて、これかよ。 ~~~~~~~~~~~~~~~~~~~~ わかったよ、一般向けにFor Each版に修正するよ。 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Private Sub 詳細_Format(Cancel As Integer, FormatCount As Integer)
    Dim objITEM As Object  'コントロールのオブジェクトを入れる変数
    If (Me![cntA] Mod 3) = 1 Then  '左端なら
        For Each objITEM In Me.Controls 'コントロールを取り出しループする
            If Left(objITEM.Name, 3) = "lab" Then '名前はラベル
                objITEM.Visible = Not pFLG
            Else
                objITEM.Visible = pFLG
            End If
        Next

        If pFLG = False Then 'まだラベルエリアを印刷してなかったら
            Me.NextRecord = False      'レコードの移動をまず止める
            pFLG = True  '印刷フラグを立てる
        End If
    Else
        pFLG = False  'その他の時
    End If

End Sub
これなら、ラベルと実データの切り替えも、 コントロール名の規則にしたがって作成すれば、 変更の少ないプログラムで済みますね。

/* * 6.終わりの挨拶(次回はアルのか?(爆)) */

よくわからなかったけど、何が今日は言いたかったの? えっと、 Ctrl+C , Ctrl+V でサクサク?コーディングするんだけど、 ちょっと待てよ、 コピー、貼り付け、一部変更、 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ なんてプログラムには、一工夫する余地があるのでは? って話でした。 まぁ、キーボード打つのが早い、エディター使うのが早い そんな若いプログラマーにトータル的に勝つには、 変更が来たとき、キーボード打量を減らす(もしくは打たなくていい) そんなプログラム作りたいよね、 30代のキーボード打ち込み遅いおっさんプログラマーとしては(爆) 何か読者の心に残れば、うれしいです。 *私の独り言をうまく消化してくださいね。 手前味噌プログラム発表したいよねのKen3でした。 ~~~~~~~(↑オイオイ)

別件サンプル: Array関数で配列を作って、処理をまとめてみました。

[No.180 IE操作 .parentElement で親のタグを参照?] ↑の IE ドキュメント オブジェクトの操作で、
始値 高値 安値 出来高
の処理で、同じIf文を使って同じような処理を行ってます。

三流以外の普通のプログラマーなら、処理をまとめます。
同じ処理なら、配列とループの出番ってことで、
Array 関数を使用して、処理をまとめてみたいと思います。

で、作成したのが↓こんな感じです。

'銘柄コードを入力後、
'IE を 起動して Yahooファイナンスのページを表示する
'始値 高値 安値 出来高 を探す、
'見つけたら 値をイミディエイトにテスト表示
'Windows XP IE7 Excel2003 で テスト
Sub test_0314_KABUKA_GET_TEST()
  '銘柄コードの入力
    Dim strCODE  As String      '銘柄コード受け取り用
    strCODE = InputBox("銘柄コード", "コード入力", "6723") '手抜きでInputBox関数を使用
  
  'IEを起動して、Yahooファイナンスのページを表示する
    Dim objIE    As Object  'IEオブジェクト参照用
    Set objIE = CreateObject("InternetExplorer.application") 'IEのオブジェクトを作る
    objIE.Visible = True '見えるようにする(お約束)

    '.Navigate で 指定した文字列のURLを開く
    objIE.Navigate "http://stocks.finance.yahoo.co.jp/stocks/detail/?code=" & strCODE

    '表示終了まで待つ .Busy(忙しい)間 と.ReadyState(ステータス)が4以外の時 ループ
    Do While objIE.Busy = True
         DoEvents  '特に何もしないで.Busyの状態が変わるまで待つ
    Loop
    Do While objIE.ReadyState <> 4
         DoEvents  '特に何もしないで.ReadyStateの状態が4に変わるまで待つ
    Loop

  'DTデータから[始値] などを探す
    Dim objParent As Object     '親のタグ オブジェクト
    Dim objTagSTRONG As Object  'STRONG の タグを保存する変数

    '.tags("DT") で DTタグを抜き出す
    Dim objDT As Object 'DTの格納用
    Set objDT = objIE.document.all.tags("DT")  '.tags("DT")でDTタグを抜く
    
    'いろいろなループを作れるけど、カウンタ n でまわしてみる
    Dim n As Integer
    Dim x As Integer
    Dim BOX As Variant

    BOX = Array("始値", "安値", "高値", "出来高")

    For n = 0 To objDT.Length - 1  'カウンタ0から.length - 1 までまわす。
        For x = 0 To 3
            If Left(objDT(n).InnerHTML, 1 + Len(BOX(x))) = BOX(x) & "<" Then '[始値<]を探す
                Set objParent = objDT(n).parentElement  '親オブジェクトを代入
                '次にSTRONG 強調表示の値を探す
                Set objTagSTRONG = objParent.all.tags("STRONG")  '.tags("STRONG")でSTRONGタグを抜く
                '値の表示
                Debug.Print BOX(x) & " = " & objTagSTRONG.Item(0).InnerText   'STRONG タグ のテキスト
            End If
        Next x
    Next n
    

  '終了処理
    '後始末(使った食器はキレイにしてから戸棚に戻そうね)
    Set objDT = Nothing
    
    'objIE.Quit    '終了処理  ← テストで残したかったので、コメントにした。
    'Set objIE = Nothing
    
    MsgBox "終了しました"
    
End Sub
ポイントは、 BOX = Array("始値", "安値", "高値", "出来高") で、変数BOXを文字列で初期化します。 For n = 0 To objDT.Length - 1 'カウンタ0から.length - 1 までまわす。 あとは、0から3のループを作り、 For x = 0 To 3 If Left(objDT(n).InnerHTML, 1 + Len(BOX(x))) = BOX(x) & "<" Then ↑BOX(x)を使用して、条件文を変更しました。 Set objParent = objDT(n).parentElement '親オブジェクトを代入 '次にSTRONG 強調表示の値を探す Set objTagSTRONG = objParent.all.tags("STRONG") '.tags("STRONG")でSTRONGタグを抜く ↑ここは、変わりなく、単純に親タグからSTRONGを探してます。 '値の表示 Debug.Print BOX(x) & " = " & objTagSTRONG.Item(0).InnerText ↑ここは、見出しの文字を Box(x) で使用してます。 End If Next x Next n 読者の声で 初めから 書けよ。。。と言われそうな。。。
03/14 Array 関数を使用して処理をまとめる
http://www.youtube.com/watch?v=Faq5AuMRliA

三流君のHP主な飛び先

VBA系のメルマガで書いた解説を項目ごとにまとめてます

[VBAでAccessを操作(一般)]
[VBAでAccessのレポートを操作]
[VBAでAccessのUserForm/サブフォームを操作]
[VBA Access から Excel 連携]

[VBA でExcel オブジェクト/プロパティ/メソッド/一般]
[VBA でExcel UserForm(ユーザーフォーム)を操作する]
[VBA ExcelからAccessを操作する]

[VBA Outlookの操作]
[VBA IEの操作]
[VBA テキストファイル(*.txt,*.html,*.csv)の操作]
[VBA標準関数関係とその他解説]

ASP系の解説を項目ごとにまとめてみました

[ASP Form等を使用したデータのやり取り]
[ASP その他処理サンプル]
[ASP テキストファイル処理]
[ASP VBScript関数関係の説明]
[ASP ADOでMdbファイルを使う]
[ASP ADOでExcelと接続してみた]