配列状データをまとめてシフトするには?

解決


紅閃光  URL  2007-11-23 18:57:42  No: 99794  IP: 192.*.*.*

開発環境はWinXP+VB6SP6です。

ユーザー定義型を配列にしたデータがあります。
配列内のデータをインデックス番号をシフトさせる方法にはどのような方法があるでしょうか?

例:)
  Data(0)=12
  Data(1)=345
  Data(2)=67
  Data(3)=890
  Data(4)=123
    ↓    
  Data(0)=0
  Data(1)=12
  Data(2)=345
  Data(3)=67
  Data(4)=890

これはただの配列変数ですが、ユーザー定義型の配列変数で同じような事をします。
もちろん、ループをまわしてインデックス+1でもすれば可能なのはもちろんですが、
ループを使わず、高速で安全な方法があれば教えてください。


とりあえず、ミジンコのような脳みそを振り絞ってユーザー定義型でうにゃうにゃやってみました。


Public Const MAX_DATA_NUM = 1000
Public Const Shift_UP = 1
Public Const Shift_2UP = 2
Public Const Shift_Down = -1
Public Const Shift_2Down = -2

Private Type GraphFormat    'この例ではグラフ4本分のデータ
    Time As Date
    RateE As Single
    RateM As Single
    RateTcE As Single
    RateTcM As Single
End Type

Private Type GraphMemoryFormat
    No(MAX_DATA_NUM) As GraphFormat
End Type

Private Type MoveAfterFormat
    Data As GraphMemoryFormat
    Dummy As GraphFormat
End Type

Private Type MoveBeforeFormat
    Dummy As GraphFormat
    Data As GraphMemoryFormat
End Type

Private GraphMemoryData As GraphMemoryFormat


Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" _
    (Destination As Any, Source As Any, ByVal Length As Long)


Public Sub Form_Load()
    Dim i As Long
    For i = 1 To MAX_DATA_NUM + 1
        With GraphMemoryData.No(i - 1)
            .Time = i        ' 確認用ダミーデータ
            .RateE = i * 10
            .RateM = i * 100
            .RateTcE = i * 1000
            .RateTcM = i * 10000
        End With
    Next
    
    Call GraphDataShift(Shift_UP)
'    Call GraphDataShift(Shift_Down)    '逆方向
End Sub


Public Sub GraphDataShift(ByVal Direction As Long)
    Private MoveAfter As MoveAfterFormat
    Private MoveBefore As MoveBeforeFormat

    Select Case Direction 
    Case Shift_UP
        MoveBefore.Data = GraphMemoryData
        Call CopyMemory(MoveAfter, MoveBefore, Len(MoveBefore))
        GraphMemoryData = MoveAfter.Data
    Case Shift_Down 
        MoveAfter.Data = GraphMemoryData
        Call CopyMemory(MoveBefore, MoveAfter, Len(MoveAfter))
        GraphMemoryData = MoveBefore.Data
    End Select 
End Sub



これで一応期待通りの動作はします。

定義が三重ネストになっているのが少々不満ですが。


ただし、問題がありまして、ユーザー定義型の制限で64kByteまでしか定義できないことと、データーの配列数(GraphMemoryFormatの定義)を実行中に変更できない点とです。

まぁ、上限の64kByteのほうはまだいいのですが、あらかじめ MAX_DATA_NUM を多くしすぎると無駄にメモリを消費します。
データの列数が少なくても良いときには、変数のサイズはコンパクトにしたいところです。

それから、CopyMemory API関数はVB6でユーザー定義型に配列が含まれているとメモリリークする、とどこかの質問掲示板に書かれていたのを見かけたのですが、だとすれば上記のコードはちょっとつかえません。本当でしょうか?


ということで、以下の観点から、ほかにいい方法はないでしょうか?

1)ループは使わない(コードの質、量による)
2)DATA_NUMを実行中に変更できるようにしたい
3)変数による消費メモリはデーターの個数に比例したい
4)高速であればあるほどうれしい

全部満たすものというわけではなく、どれかに特化した方法でまったくかまいません。
また、上記のわたしのコードに対する非難轟々(改善点)も大歓迎です。
アクロバティックなウルトラC(コード)でもどんとこいです。

情報お待ちします。

変数名がNoGoodなのは勘弁してくださいな><

編集 削除
魔界の仮面弁士  2007-11-23 20:16:36  No: 99795  IP: 192.*.*.*

メモリコピーはお奨めできません。
ユーザー定義型メンバのアライメント調整によって、
Len(x) = LenB(x) になるようなデータ型の場合に問題が出ますし。


そもそも、ループを使うのは真っ当な解決策だと思うのですが、
それを嫌うのには、何か理由があるのでしょうか?


> 64kByteまでしか定義できないことと
ユーザー定義型でなければいけないのでしょうか? (クラスでは駄目ですか?)
たとえば ADODB.Recordset なら、ソートも検索も簡単に書けますよ。

Option Explicit

Private GraphMemoryData As ADODB.Recordset

Private Sub Form_Load()
    'GraphFormat 型のデータ構造を定義
    Set GraphMemoryData = CreateGraphFormat()

    '100件のランダムデータを追加
    FillRandom GraphMemoryData, 100

    '確認用に画面に表示
    Set DataGrid1.DataSource = GraphMemoryData

    Command1.Caption = "ダンプ"
    Command2.Caption = "正RateE,逆RateM"
    Command3.Caption = "逆Time"
End Sub

Private Sub Command1_Click()
    '現在の内容を、イミディエイト ペインに出力
    GraphMemoryData.MoveFirst
    Debug.Print GraphMemoryData.GetString(, , "|", vbCrLf)
End Sub

'★ ソート手順 ★
Private Sub Command2_Click()
    'RateE の昇順、RateM の降順にソート
    GraphMemoryData.Sort = "RateE ASC, RateM DESC"
End Sub
Private Sub Command3_Click()
    'Time の降順にソート
    GraphMemoryData.Sort = "Time DESC"
End Sub

'★ GraphFormat ユーザー定義型のかわり ★
Private Function CreateGraphFormat() As ADODB.Recordset
    Set CreateGraphFormat = New ADODB.Recordset
    With CreateGraphFormat.Fields
        .Append "Time", adDate
        .Append "RateE", adSingle
        .Append "RateM", adSingle
        .Append "RateTcE", adSingle
        .Append "RateTcM", adSingle
    End With
    CreateGraphFormat.Open
End Function

' count 件のランダムなデータを生成
Private Sub FillRandom(ByVal rs As ADODB.Recordset, ByVal count As Integer)
    Dim n As Integer
    Dim d As Date
    d = Now
    For n = 1 To count
        rs.AddNew _
            Array("Time", "RateE", "RateM", "RateTcE", "RateTcM"), _
            Array(DateAdd("s", n, d), Random(), Random(), Random(), Random())
    Next
End Sub

'-50.0 〜 +50.0 の範囲の乱数を返す
Private Function Random() As Single
    Random = Int(500 * Rnd() + -500) / 10
End Function

編集 削除
魔界の仮面弁士  2007-11-23 20:21:25  No: 99796  IP: 192.*.*.*

一か所修正 m(_ _)m
# 本題とは関係ないところですが。

> '-50.0 〜 +50.0 の範囲の乱数を返す
Private Function Random() As Single
    Random = Int(1001 * Rnd() + -500) / 10
End Function

編集 削除
魔界の仮面弁士  2007-11-23 21:20:27  No: 99797  IP: 192.*.*.*

読み違えてました。
ソートではなく、固定件数に対する単純なシフトでしたか。

だったら、Recordset なら
For n = 1 To limit
    rs.MoveFirst
    rs.Delete
    rs.AddNew
Next
でシフトできますが…逆シフトは、Sort と組み合わせないと駄目ですね。
# AddNew 直後のレコードは、AbsolutePosition が末尾値にしかならないし。


登録時も利用時もループ無しというのは難しいかな…。

.NET でいうところの、ArrayList や List(Of T) に相当する物が
用意されていれば良いのですが、VB6 でそれに相当するのは無さそうで。
(ListBox や TreeView などは、それに近いことができますが)

さて、どうするか。

編集 削除
ガッ  2007-11-23 21:55:54  No: 99798  IP: 192.*.*.*

※メモリ的に「一つずらす」のであれば、下に書いてあるものは無意味なのでスルーしてください

リングバッファを用いてそれっぽく見せるクラスを定義してはどうでしょうか?

'例
Option Explicit

Public Enum eShiftDirection
    shift_Left
    shift_Right
End Enum

Private innerArray() As Long
Private innerArraySize As Long
Private start As Long

Public Sub Initialize(ByVal size As Long)
    ReDim innerArray(0 To size - 1)
    innerArraySize = size
    start = 0
End Sub

Private Function idx2innerIdx(ByVal idx_base1 As Long) As Long
    idx2innerIdx = (start + (idx_base1 - 1)) Mod innerArraySize
End Function

Public Function size() As Long
    size = innerArraySize
End Function
Public Property Get itemOf(ByVal idx_base1 As Long) As Long
    If idx_base1 < 1 Or innerArraySize < idx_base1 Then Err.Raise 9
    itemOf = innerArray(idx2innerIdx(idx_base1))
End Property

Public Property Let itemOf(ByVal idx_base1 As Long, ByVal nv As Long)
    If idx_base1 < 1 Or innerArraySize < idx_base1 Then Err.Raise 9
    innerArray(idx2innerIdx(idx_base1)) = nv
End Property

Public Sub shift(ByVal direction As eShiftDirection, ByVal n As Long, Optional ByVal clearValue As Long = 0)
    If n < 0 Then Err.Raise 9
    If n > innerArraySize Then
        '初期化
        ReDim innerArray(0 To innerArraySize - 1)
    Else
        'シフト
        Select Case direction
            Case eShiftDirection.shift_Left
                For n = n To 1 Step -1
                    start = start - 1: If start < 0 Then start = innerArraySize - 1
                    innerArray(start) = clearValue
                Next
            Case eShiftDirection.shift_Right
                For n = n To 1 Step -1
                    innerArray(start) = clearValue
                    start = start + 1: If start >= innerArraySize Then start = 0
                Next
        End Select
    End If
    
End Sub

編集 削除
魔界の仮面弁士  2007-11-23 22:00:54  No: 99799  IP: 192.*.*.*

> アクロバティックな
トリッキーな方向に持っていってみました。


> VB6 でそれに相当するのは無さそうで。
JScript の Array オブジェクトを利用したコード。

'-----------
'Class1
Option Explicit
Public Time As Date
Public RateE As Single
Public RateM As Single
Public RateTcE As Single
Public RateTcM As Single
'-----------

'-----------
'Form1
Option Explicit

Private SC As Object
Private List As Object

Private Sub Form_Load()
    '50件のコレクション作成
    Set SC = CreateObject("ScriptControl")
    SC.Language = "JScript"
    SC.ExecuteStatement "List=new Array()"
    Set List = SC.Eval("List")
    Dim n As Integer
    For n = 1 To 50
        List.push NewClass1()
    Next

    Command1.Caption = "ダンプ"
    Command2.Caption = "2件下シフト"
    Command3.Caption = "2件上シフト"
End Sub

'ダンプ
Private Sub Command1_Click()
    Dim c As Class1
    Dim n As Integer
    n = 0
    For Each c In List
        n = n + 1   '件数表示用
        Debug.Print n, c.Time, c.RateE, c.RateM, c.RateTcE, c.RateTcM
    Next
End Sub

'2件下シフト
Private Sub Command2_Click()
    '末尾2件を除去
    SC.ExecuteStatement "List.pop()"
    SC.ExecuteStatement "List.pop()"
    
    '先頭に空データ2件挿入
    List.unshift New Class1
    List.unshift New Class1
End Sub

'2件上シフト
Private Sub Command3_Click()
    '先頭2件を除去
    SC.ExecuteStatement "List.shift()"
    SC.ExecuteStatement "List.shift()"

    '末尾に空データ2件挿入
    List.push New Class1
    List.push New Class1
End Sub

Private Function NewClass1() As Class1
    Set NewClass1 = New Class1
    With NewClass1
        .Time = Now()
        .RateE = Random()
        .RateM = Random()
        .RateTcE = Random()
        .RateTcM = Random()
    End With
End Function
Private Function Random() As Single
    Random = Int(1001 * Rnd() + -500) / 10
End Function
'-----------

編集 削除
紅閃光  2007-11-23 22:12:03  No: 99800  IP: 192.*.*.*

お早い回答ありがとうございます。

> Len(x) = LenB(x) になるようなデータ型の場合に問題が出ますし
Len(x) <> LenB(x) のとき、でなくてですか?

メモリコピーの問題は、アライメントの問題だけなんですか?
まぁずれたらデータがバケルだけで、メモリリークとかは関係無いですよね。


> それを嫌うのには
いえ、ループを使ってやる方法なら淡々とできるのですが、それだと技術的なおもしろさが無いので、なにか別のアプローチがないかと考えたところです。

別にループを嫌っているわけではないです。
件数が多くなるとパフォーマンス的に不利なのかな、とは思いましたが。
ループを使ったほうが合理的かつ速やかに終わるならそっちを使います。


> ユーザー定義型でなければいけないのでしょうか
特にユーザー定義型である必要自体はないです。
なるほど、ADODB.Recordset ですか。
今回はソートではなく、データ行の削除が出来れば十分ですけどね。
ちょっとこの方法を勉強させてもらいます。


やろうと思えばListBOXでも同じような事ができますね。
まぁ型の指定が出来たほうがベターですが。

編集 削除
魔界の仮面弁士  2007-11-24 13:31:03  No: 99801  IP: 192.*.*.*

> Len(x) <> LenB(x) のとき、でなくてですか?
そのとおりです。m(_ _)m

で、アライメントを意識してコピーする分には問題ありませんが、
そこに気を使うぐらいなら、手動コピーの方がスマートかな、と。


> アライメントの問題だけなんですか?
データ型によっては、実データ部以外の場所に管理情報を保持している
場合もありますので、やはりおすすめできません。
(Date や Currency 等の単純型なら良いのですが)


極端な例で言えば、こんなのとか。

Option Explicit

Private Type UDT
    Member As Object
End Type

Public Declare Sub RtlMoveMemory Lib "kernel32" _
(ByRef Destination As Any, ByRef Source As Any, ByVal Length As Long)

#Const ManualCopy = True

Sub Main()
    Dim a(1) As UDT
    Set a(0).Member = CreateObject("Excel.Application")
    a(0).Member.Visible = True
    Debug.Print VarPtr(a(0).Member), ObjPtr(a(0).Member)
    Debug.Print VarPtr(a(1).Member), ObjPtr(a(1).Member)
    
    Debug.Print "-- copy ---"
    #If ManualCopy Then
    Set a(1).Member = a(0).Member
    #Else
    RtlMoveMemory a(1), a(0), Len(a(0))
    #End If
        
    Debug.Print VarPtr(a(0).Member), ObjPtr(a(0).Member)
    Debug.Print VarPtr(a(1).Member), ObjPtr(a(1).Member)

    Debug.Print "-- reference test --"
    Debug.Print a(0).Member.Version
    Debug.Print a(1).Member.Version

    Debug.Print "-- release test --"
    Set a(1).Member = Nothing   '★
    a(0).Member.Quit
    Set a(0).Member = Nothing
    Debug.Print "-- finish --"
End Sub

アドレス参照だけで見れば、手動コピーも API コピーも同じ結果ですが、
API コピーの場合、参照カウントの管理が行われないため、★ 以降の
処理結果に違いが出てしまいます。
# オブジェクト型のほか、可変長文字列型やバリアント型も注意が必要かと。


> 特にユーザー定義型である必要自体はないです。
だとすれば、やはりクラス等で管理した方が都合が良いと思います。

インデックスの管理情報をループでずらすにしても、ユーザー定義型では
API コピーまたは Let によって実データを複写する必要がありますが、
オブジェクトなら Set ステートメントで参照だけを移せば済みますし、
サイズ制限についてもクリアできますから。

編集 削除
GOD  2007-11-26 09:51:43  No: 99802  IP: 192.*.*.*

>'Class1
>Option Explicit
>Public Time As Date
> :
>Public RateTcM As Single
>
としてコレクションで管理するとか。
ループコピーよりは速そうだけどメモリ消費は大きそう。
まぁ、ユーザー定義型のしばりがない時点で求めてるの違いそうwww

編集 削除
紅閃光  URL  2007-11-28 00:45:35  No: 99803  IP: 192.*.*.*

あ、ごめんなさい><
前回のわたしの書き込みは2007/11/23(金)21:20:27以降の3件を見ていないで書き込んでいました。


> ソートではなく、固定件数に対する単純なシフトでしたか。
> だったら、Recordset なら
> For n = 1 To limit

このlimitとは、シフト件数とイコールですか?
引数無しのAddNew の場合、レコードの内容は、各フィールドの型の初期値ということになるんですかね。
今回に限ればシフト方向は片側でも十分といえば十分です。
また、ソートをしたとして、かかる時間はデータ量に比例するなら、大量データはパフォーマンス的に難しいですね。


> ※メモリ的に「一つずらす」のであれば、下に書いてあるものは無意味なのでスルーしてください

もちろんデーターそのものにアクセスしたいだけなので、メモリ的にはどうでもいいです。
なるほど、データーのほうでなく開始アドレスをずらす方法はいいですね!

と、思ったら、クラス内なのでinnerArray周りを単純にユーザー定義型(As GraphFormat)にすればOKというわけにはいかないですよね。

項目の数だけプロパティを追加する形になるのかな。
そうならshiftメソッドにclearValue は実装できないのが少し残念です。



> JScript の Array オブジェクトを利用したコード。

これってJAVAが実行できる環境でないとダメなんでしょうか?
まぁ、よっぽどのことがないと最近のパソコンなら実行できそうですけど・・・
ループコピーよりは速そうだけどメモリ消費は大きそうなんですか?



> データ型によっては、実データ部以外の場所に管理情報を保持している場合もありますので

あー、確かにオブジェクト型は何がおきるか危険ですね。
ポインタとかが化けたりずれたら容易にガガーリン@宇宙飛行士が降臨しそうです。
なんにしろ、メモリコピーは限定的に使うべし、と。

編集 削除
ガッ  2007-11-29 04:43:47  No: 99804  IP: 192.*.*.*

# Re: 紅閃光 [HomePage] 2007/11/28(水) 00:45:35

> と、思ったら、クラス内なのでinnerArray周りを単純にユーザー定義型(As GraphFormat)にすればOKというわけにはいかないですよね。
多分 GraphFormat をグローバルモジュール(でしたっけ?)に配置すればイイ化と思います。
いちいちActiveXとして公開する(これも、多分)のが面倒なら、クラスにしてしまえばいいですし。


ところで、Collection クラスは使えないでしょうかね?
※(さらに、多分)使えるはず

編集 削除
魔界の仮面弁士  2007-11-29 09:15:45  No: 99805  IP: 192.*.*.*

> 引数無しのAddNew の場合、レコードの内容は、各フィールドの型の初期値ということになるんですかね。
初期値は、特に指定していなければ、Null または Empty です。


> また、ソートをしたとして、かかる時間はデータ量に比例するなら、大量データはパフォーマンス的に難しいですね。
オンメモリ Recordset の Sort は、データ量が多くても比較的高速に処理されます。
また、検索や並べ替えを頻繁に行う場合は、
  rs.Fields(0).Properties("Optimize").Value = True
のように、Optimize 動的プロパティ を設定してインデックスを作成すると、
パフォーマンスが向上します。

>> JScript の Array オブジェクトを利用したコード。
> これってJAVAが実行できる環境でないとダメなんでしょうか?
JAVA … ですか? そもそも JAVA と JScript は、直接の互換性は無く、まったく無関係です。
ScriptControl が使える環境であれば、JScript も漏れなくインストールされているでしょうから、特に問題なく使えるかと。


とはいえ、単方向シフトで良いならば、ガッさんが書かれている VBA.Collection の方がお奨めです。

編集 削除
紅閃光  URL  2007-11-30 08:01:13  No: 99806  IP: 192.*.*.*

> ところで、Collection クラスは使えないでしょうかね?

使ったことがないのですが、今回は別に使う必要は無いのでは?
    Private innerArray() As clsGraphFormat 
とだけしておけば、リングバッファだから項目の追加や削除は必要ないかと。

あれ?でも、
    ReDim innerArray(0 To size - 1) 
としたとして、インスタンスはいつ、どのように行えば・・・

もしかしてこうとか。
    For i = 0 To size - 1
        Set innerArray(i) = New clsGraphFormat
    Next

んでもって開放のときは
    Dim Obj As Variant
    For Each Obj In innerArray
        Set Obj = Nothing
    Next

えっ、なんか・・・


こういう場合にCollection を使えということでしょうか?

    Private cInnerArray As Collection

    Set cInnerArray = New Collection
    Dim dummy As New clsGraphFormat    
    For i = 0 To size - 1
        dummy.RateE = i
        cInnerArray.Add dummy
    Next


・・・わたしが勘違いしてるんでしょうか><



>> JScript の Array オブジェクトを利用したコード。

あ、JAVAとは関係ないんですね。勘違いです><
ScriptControl が使えればOKと。


>とはいえ、単方向シフトで良いならば、ガッさんが書かれている VBA.Collection の方がお奨めです。

もう少し調べてみます。
が、リングバッファでもいいのでは、と思ったりするんですが。

編集 削除
魔界の仮面弁士  2007-11-30 09:26:04  No: 99807  IP: 192.*.*.*

100 個登録するときは、

Private list As VBA.Collection
Private Sub Form_Load()
    Set list = New VBA.Collection
    Dim n As Integer
    For n = 1 To 100
        list.Add CreateClass1(123.456, …)
    Next
End Sub

Public Function CreateClass1(ByVal rateE As Single, …) As Class1
    Set CreateClass1 = New Class1
    CreateClass1.rateE = rateE
        :
End Function

という感じで。


取り出すときは、list(数値).rateE などで。


インデックスのシフトについては、こんな感じ。

'登録済みの先頭2件 list(0), list(1) を削除し、
'末尾に、初期値で2件 list(99), list(100) 追加
list.Remove 1
list.Add New Class1    

list.Remove 1
list.Add New Class1

編集 削除
GOD  2007-11-30 10:07:09  No: 99808  IP: 192.*.*.*

>使ったことがないのですが、今回は別に使う必要は無いのでは?
>
必要であるかないかは該当者にしか決められないことです。
ただ、GraphFormatがクラス化できるのであればコレクションで管理させた方が楽ですよ。

> 魔界の仮面弁士さん
>
名指しすみません。
ちょっと気になったことがあるのですが「単方向シフト」とは何でしょうか。
Add メソッドの Before, After を使用すれば任意のとこに挿入できると思うのですがそう言うことではないのかな?

編集 削除
魔界の仮面弁士  2007-11-30 10:42:25  No: 99809  IP: 192.*.*.*

》GODさん
> Before, After を使用すれば任意のとこに挿入できると思うのですがそう言うことではないのかな?

おぉぉぉ、そういえばそうですね。フォローありがとうございます。
ということは、Collection を使うのが一番スマートかな。


> '登録済みの先頭2件 list(0), list(1) を削除し、
> '末尾に、初期値で2件 list(99), list(100) 追加

これの逆方向シフトは…

'末尾2件を削除し、先頭に2件新規追加
list.Remove list.Count
list.Add New Class1, Before:=1

list.Remove list.Count
list.Add New Class1, Before:=1

編集 削除
紅閃光  URL  2007-11-30 19:28:16  No: 99810  IP: 192.*.*.*

やっと理解しました。
オブジェクト変数をうにゃうにゃした経験も無かったので、とりあえず実際にリングバッファ(オブジェクト変数)版を作ってみました。
実行中に件数を増減出来るようにするための手間がかなり煩雑になりました。

コレクション版ならインデックスポインタを管理する必要もないですし、件数の増減もデータを保持したまま出来ますね。
リングバッファでは気に食わないところがすっきりしました。


ところで、コレクションの削除(Nothing)はしたほうがいいのでしょうか?
さすがにコレクションの中身は全件Removeしなくてもいいとは思いますが・・・どうなんでしょう?

編集 削除
魔界の仮面弁士  2007-11-30 19:43:45  No: 99811  IP: 192.*.*.*

> ところで、コレクションの削除(Nothing)はしたほうがいいのでしょうか?
Collection 変数がスコープから外れれば、自動的に解放されます。

データ量が多く、何十、何百メガバイトもの容量を使っているならば、
早期解放も必要でしょうけれど、通常は強制解放は不要かと。


解放漏れが心配なら、クラスモジュール内に
  Option Explicit
  Private Sub Class_Initialize()
    Debug.Print "-- Init --> "; ObjPtr(Me), TypeName(Me)
  End Sub
  Private Sub Class_Terminate()
    Debug.Print "<-- Term -- "; ObjPtr(Me), TypeName(Me)
  End Sub
のようなコードを埋め込んでおけば良いでしょう。

クラスのインスタンスが解放されれば、"<-- Term -- " のメッセージが
出力されます。

# (開発時ではなく)コンパイル後にログが欲しいなら、App.LogEvent メソッドで。

編集 削除
紅閃光  URL  2007-12-01 20:07:44  No: 99812  IP: 192.*.*.*

ということは、基本的に参照カウンタが有効に働いているオブジェクトは、スコープが無くなったときに勝手に解放されるので、早めに開放したいという理由が無ければほっとけば良いということですね。


ちなみにシフト部はガッさんのを参考に以下のようにしてみました。

Public Sub DataShift(Optional ByVal n As Long = 1, Optional ShiftDirection As eShiftDirection = shift_Up)
    If n < 0 Then Exit Sub                          
    If n > mList.Count Then n = mList.Count  
        
    Select Case ShiftDirection
        Case eShiftDirection.shift_Up                   ' 順方向送り
            For n = n To 1 Step -1
                Call mList.Remove(mList.Count)
                Call mList.Add(CreateGraphFormat, Before:=1)
            Next
        Case eShiftDirection.shift_Down                 ' 逆方向送り
            For n = n To 1 Step -1
                Call mList.Remove(1)
                Call mList.Add(CreateGraphFormat)
            Next
    End Select
End Sub


< For Num = Num To 1 Step -1
ってあたりが小憎らしいですね。
今後パクらせていただきます  (・ー・)b


今件の起こりは、たとえば件数が増えて1万×10個のデータがあるとして、データの単純な一括シフトで10万回変数にアクセスするのがカコワルイ(もちろん独断と偏見)と思ったからなんです。

提示していただいた方法を含めると
・ユーザー定義型のアライメントを利用してメモリコピーする
・リストコントロールで管理する
・データーベース(レコードセット)として管理する
・オブジェクト配列のリングバッファとしてインデックスポインタを管理する
・コレクションとして管理する

どれでも必要な機能は実装できましたが、コレクションを利用する方法が一番シンプルでパフォーマンスも悪くないという結果になりました。


コレクションはかなり使い道がありそうですねぇ。
以前、ユーザー定義型変数の配列でデータ管理しようとして64kByteの壁にぶち当たって泣く泣く分割したことがあります。
クラスを定義してコレクションにしてしまえばスマートですね。



なにはともあれ、とりあえずは目的達成ですので、ひとまず解決とさせていただきます。

まだ、別の面白い(?)方法があれば書き込みお願いします。

魔界の仮面弁士さん、GODさん、ガッさん、ありがとうございました。
今回も勉強になりました。



わたしのメモリコピーが一番アクロバティックでしたね。  (´▽`*)アハハー・・・

編集 削除