• 締切済み
  • 暇なときにでも

VBAでオブジェクトの配列の配列の削除をすると動作異常になります

VBA(Excel2000)でオブジェクト指向のお勉強をしておりますが、ミスか言語仕様か分からないトラブルがあり、困っております。 簡単な碁(碁盤を再現し、活き死に程度を判定する)のプログラムを作成していますが、クラス石の配列であるクラス群を作成し、更にクラス群の配列を石の黒・白別に標準モジュールで定義しています。一塊の石の群が死んでいると判定した時、その群を削除しようとするのですが、redim preserve 群の配列(添字-一番最後の)を実行すると、群の配列全体にアクセスできなくなってしまいます。redimする前に、Nothingを代入するとか、もとの群(石の配列)にNothingを代入してからredimするとかしてみましたが、事態は変わりませんでした。 仕様なのか、勘違いしているのか、クラスの使い方が分かっていないのか..アドバイスお願いします。

共感・応援の気持ちを伝えよう!

  • 回答数3
  • 閲覧数862
  • ありがとう数4

みんなの回答

  • 回答No.3

'まずは動的配列の宣言 Dim x() As Integer '配列要素の宣言 Dim y As Integer 'お次は動的配列の再宣言 y = 100 Redim x(1 to y) '…いろんな処理を経て… 'さて現在の配列から、 '88番目だけ削除しようかな…。 Dim Back_x() As Integer Redim Back_x(1 to y) Dim intLoop As Integer For intLoop = 1 To y Back_x(intLoop) = x(intLoop) Next Erase x Redim x(1 To y - 1) For intLoop = 1 To y If intLoop < 88 Then x(intLoop) = Back_x(intLoop) ElseIf intLoop > 88 Then x(intLoop - 1) = Back_x(intLoop) End If Next y = y - 1 これで1個削除できたっしょ?

共感・感謝の気持ちを伝えよう!

質問者からのお礼

なるほど。java向けの参考書をVBAに載せ替えて勉強しておりましたので、javaは配列の再編が楽そうだなと思いながら、消したい要素と一番最後の要素を入れ替えてから、redimしておりましたが、これだと順番が入れ替わってしまうので、教えて頂いた方法の方がスマートですね。勉強になりました。ところで、碁盤の例では最大4個の群を統合して一つの群にまとめ、以前の群を削除する事を行っています。これをUndoしたいと思ったものです。現在は、仕方がないので次々と新しい群を作り、石が活きていると判定できてから統合する群をNothingに置き換えていますので、どんどんとゴミが増えて行ってしまいます。良いやり方がありますでしょうか。

関連するQ&A

  • VBAでバイナリとして配列の一部を書き込む方法

    配列a(256, 256)をまるごとバイナリデータとして書き込みたい場合には Put #1,,a a(256, 256)の配列に入った数値のうち、 a(51,51)&#65374;a(256,256) に入った数値のみをバイナリデータとして書き込みたいのですが どのようにすれば良いですか? で次元を変更して、この配列の中のデータを消さないままで a(0,0)&#65374;a(50,0)とa(0,0)&#65374;a(0,50) を削除してa(206,206)の配列を作成したいのですが、 ReDim Preserve a(206,206) とすると ”インデックスが有効範囲にありません”というエラーがでます。 http://hpcgi1.nifty.com/kenzo30/b_cbbs/cbbs.cgi?mode=al2&namber=7771&rev=&no=0&P=R&KLOG=51 このページにあるように、 ReDim Preserveは一次元配列でしか使えないそうです。 どうすれば一部だけバイナリに書き込めますでしょうか?

  • 動的配列確保

    JAVA初心者です。 VBでのRedim Preserve、CのreallocみたいなものはJAVAには無いのでしょうか? 要はあらかじめ配列数がわかっていない時に動的配列確保を行いたいのです。 Objectではなく、基本クラスのint、byte等の配列に使いたいと思っております。 ArrayListが近い事が出来そうだったのですが、Objectにしか使用出来ないので、 断念しました。教えてください。

    • ベストアンサー
    • Java
  • VBAでオブジェクトの配列を使用するには?

    Access2000のVBAでプログラムをしています。 VBAでクラスを作成し、プロシージャの呼び出しの引数に使用しています。あるプロシージャで、クラスの配列を作成し、呼び出し元へ返したいと思っていますが、うまくいきません。これは、できないものなのでしょうか? ---(呼び出し側) Dim objA as clsA, aryB as variant ' 「aryB as Object」でもダメ procB objA, aryB Dim objTemp as Variant ' 「objTemp as Object」でもダメ For Each objTemp In aryB ' ここでエラー「オブジェクトが必要です」 MsgBox objTemp.AAA Next --- --- (プロシージャ側) Public Sub procB(ByRef objA as clsA, ByRef aryB as Variant) Dim aryRet() as clsB, lngI as long For lngI = 0 To 10 Redim Preserve aryRet(lngI) Dim objTemp as clsB Set objTemp = New clsB Set aryRet(lngI) = objTemp Next aryB = aryRet End Sub やりたいことは、プロシージャ側でオブジェクトの配列を作成し、呼び出し側に渡して、呼び出し側で、「For Each・・・Next」で回したいと思っています。 どのようにしたら実現できるか、知っている方、よろしくお願いします。

  • 回答No.2

Redimステートメントでは、 配列要素の上限値以下の値を 設定することが出来ません。 例えば、 Dim x() As Integer…(1) Redim x(1 to 100)…(2) Redim x(1 to 10)…(3) この場合、(3)でエラーになります。 したがって、このような処理を行いたい場合、 一度、バックアップを取った後 配列をEraseステートメントで、 クリアしてあげたうえで 元の変数に削除したい要素を除いたものを 戻してあげる必要があります。

共感・感謝の気持ちを伝えよう!

質問者からのお礼

アドバイスありがとうございます。実は一手打って、石の群を再編し、その結果石が囲まれて死んでしまう位置であったという場合に、元に戻せるようオブジェクトの配列の配列をコピーして操作して、死んでいるときは消してしまい、活きていればコピーし戻そうと考えたのですが、EXCEL VBAは参照渡しらしくうまくいきませんでした。(別の関数等にByValで渡せばとも思いましたが、うまくいかずくじけました)仕方がないので別の方法で対処しましたが、後学のため教えて頂けると幸いです。

  • 回答No.1
  • BLUEPIXY
  • ベストアンサー率50% (3003/5914)

Redimする時にちゃんと新しいサイズ上限は指定していますか? 削除するのに、preserveっておかしい気もしますが。 あと、削除するのにredimっていうのもおかしい気がします。 できたら、キモとなる部分(こちらでも検証できるだけの部分、そのクラスと、その配列、値の設定とredimする部分)を、補足していただけませんか?

共感・感謝の気持ちを伝えよう!

質問者からのお礼

ExcelのVBAでのクラスモジュール使用に独学でチャレンジしたもので、うまくいかないと仕様の所為にしてしまい、ご迷惑をおかけしました。お陰様でなんとか形になりました。ありがとうございました。

質問者からの補足

かいつまんで説明を試みます。 ishiというクラスは、色とか座標、属する群の番号等のプロパティしか持っていません。groupというクラスで、動的配列 Dim myGun() As ishi を宣言しています。質問に記した、標準モジュールで配列の配列を宣言というのは勘違いで、playerというクラスの中で、動的配列Dim myGroups() As group を宣言しています 標準モジュールは、先手と後手のplayerに順番を渡す程度の処理しかしていません。 新しく、例えば黒石を打つと、playerクラスの中で、ishi一個の新しいgroupを作り、盤上で隣接するishiがあれば、新しいgroupに取り込み、groupに活きているか死んでいるか問い合わせします。 新しいgroupを作る際に、myGroups()は、redim preserve myGroups(省略)で、一つ大きくしています。 groupから死んでいるという返事があったとき、このgroupの削除を行いたいのです。 と、コードを眺めながら記していたら、ReDim Preserve myGroups(0 To currentGunNo - 1)と すべき所、Preserveが抜けていた事が判明しました。※currentGunNo=Ubound(myGroups) これが原因かもしれません(そうだったら恥ずかしい限り-昨晩深夜さんざん悩んだのですが)。もう少しいじってみます。

関連するQ&A

  • VBAで配列のNULL判定

    VBAで下記のように配列に設定したNULL値を判定しようとしました。 Dim str() ReDim Preserve str(2) str(0) = "aaa" str(1) = Null str(2) = "bbb" 以下(1)、(2)の分岐処理ではNullと判定されませんでした。 どのように判定すれば良いでしょうか? (1) If str(1) = Null Then Debug.Print "Null値です" End If (2) If str(1) = "" Then Debug.Print "Null値です" End If

  • VBAで配列に文字列が入らない?

    以下のように配列を設定し、A(ix)の配列にdo untilでそれぞれに文字列を入れようとしておりますが、うまくいきません。 dountil の一回目では代入ができているようですが、2回目ができずに止まってしまいます。 おかしな点がありましたらご指摘いただけませんでしょうか。 Dim A() As String Dim ix As Long ix = 0 ReDim Preserve A(ix) Dim tate As Long tate = 1 Do Until tate = 8 A(ix) = ws.Cells(tate, 1) tate = tate + 1 ix = ix + 1 Loop Doの初回A(0)に文字列は入りますが、 Doの二回目以降A(1)に文字列を入れる作業ができずに止まってしまいます。 よろしくお願いします。

  • ASPのReDim Preserveについて

    いつもお世話になります。 ASP(VBScript)で教えて頂きたいことがあります。 VBScriptにはJavaでいうArrayListのようなコレクション機能がないので、動的配列を実現するためには ReDim PreserveやScripting.Dictionaryを使用しなければなりませんよね。 そこで、DBから取得したレコードを、ループする度毎にReDim Preserveを使用し、データをセットするよう な実装をしていました。すると、ReDim Preserveは問題があるのでやめた方がよい、と言われました。 具体的には、ReDim Preserveをすることによって、新たに配列を作成し、古い内容をコピーするため、 倍々にメモリを食いつぶしていくことになり、それが大きくなるとどんどん遅くなっていき、そしてメモリリーク になる、ということでした。 自分なりに調べてみたのですが、そのような記事を見つけることができませんでしたし、JavaのArrayList でも内部的には配列を持っており、配列の再作成などをやっています。 ○○百万件などといった大量件数を扱う際は、それはメモリリークにならないような設計、実装をしなければ ならないとは思うのですが、それはどの言語でも共通で言えることであって、特別にVBScriptではやめた方がよい、 とは思えません。 ReDim Preserveを使用しても、先に述べたような認識をもっていれば問題ないと思うのですが、どうでしょうか。 VBScriptは、こういう仕組みだから駄目なんだ、という理由があるのでしょうか。もしあれば、根拠となる サイトなど教えて頂けると嬉しいのですが。。。 宜しくお願い致します。

  • ジャグ配列生成時の1オリジン

    ExcelVBAで、CSVFileを取込み、シートを介さずに 配列へ格納する処理をしています。 尚、一行のデータの数が変わる可能性があるため、 ジャグ配列にしています。  Dim FileNamePath As Variant 'CSVファイルパス  Dim CSVFile() As Variant  Dim ch1 As Long  Dim RowCnt As Long ~略~  ch1 = FreeFile  Open FileNamePath For Input As #ch1  RowCnt = 1  Do While Not EOF(ch1)   ReDim Preserve CSVFile(RowCnt)   Line Input #ch1, CSVFile(RowCnt)   CSVFile(RowCnt) = Replace(CSVFile(RowCnt), """", "")   CSVFile(RowCnt) = Split(CSVFile(RowCnt), ",")   RowCnt = RowCnt + 1  Loop ~略~ この時、後の処理でやりやすくするために配列の添字を 1からにしたく、行の添字となるRowCntを1としています。 同様に列となる添字も1からとしたくて、モジュールの宣言にて 「Option Base 1」としましたが、上記コードでジャグ配列を 生成すると、(多次元配列で言う)2次元目の添字は 0からとなってしまいます。  例:CSVFile(1)(0) このような状況で、ジャグ配列でも1オリジンとするには どのようにすれば良いのでしょうか。 宜しくお願い致します。

  • 配列プロパティをREDIMする方法はないのでしょうか?

     タイトルの通りなんですが、 配列として持っているプロパティがあります。 そのプロパティをREDIMしたいのですが、 これは可能なのでしょうか? VB6.0の話になります。  『クラスモジュールに表記しています』 '詳細クラス Public Property Get PK_SYOUSAI() As clsSyousai PK_SYOUSAI = K_SYOUSAI() End Property Public Property Let PK_SYOUSAI(ByVal strValue As clsSyousai) K_SYOUSAI = strValue End Property  『標準モジュールに表記しています』 ReDim Preserve clsKouSei_K(0).PK_SYOUSAI(i) clsKouSei_K(0).PK_SYOUSAI(i) = cls_S  上記のREDIMの部分でエラーが起こり、処理が進みません。 どうすればよいのでしょうか?

  • プログラム実行中に作成したオブジェクトの動作

    初めて投稿させていただきます。 現在、コントロール・配列の学習をしていて以下のようなプログラムを作っています。 プログラムを実行するとあるファイルからデータを読み込み、そのデータ数の配列を作成。 データ数分のButtonを作成。 作成したボタンに応じて違った処理を行う。 このときにボタンに応じた処理を行えずに困っています。MSDNなどでは作成したオブジェクトが1個のときの処理(AddHandler~addressOf・・・)はあったのですが、これに引数をつけたり、複数の作成したコントロールに対しての処理が見つかりませんでした。 下に作成途中のサンプルを掲載しますので、ご支援おねがいします Dim myButton as New new_button() '//ボタンを継承したクラス Dim Array(5) As String      '//データ格納配列 ***データの読み込み(データ数(item_cnt))*** ReDim Preserve Array(item_cnt) For i = 0 to item_cnt myButton = new New_button() myButton.Text = "Button" & i Me.Controls.Add(myButton) AddHandler myButton.Click,AddressOf new_click Next Private Sub new_click(ByVal sender As System.Object, ByVal e As System.EventArgs) MessageBox.show("押されたのは" & ○○ & "です") End sub ここで押されたボタンごとに処理を変更したいのですが、AddHandlerで引数を付るとエラーが出ます。 もし、読み出し元のプロパティが参照できるのあれば、派生クラスでプロパティを作成、値を入れておき呼び出し元のオブジェクトを参照するっという回避方法もあるかと思いますが、方法がわかりません。 ご存知の方ご教授お願いします

  • Java:クラスのインスタンスを配列の要素にする?

    問題集で次のプログラムを見掛ました。 A[] array = {new B(), new C()}; クラスA型の配列変数arrayにクラスBとクラスCのインスタンスを要素として代入しています。 このような配列を作成した場合、具体的にどのような使い方があるのでしょうか? 簡単なプログラムを書いて頂けると嬉しいです。 よろしくお願いします。

  • 合計金額を出すには動的配列?

    初投稿です。ASPで見積スクリプトを作っています。 ソースは・・・ Set ObjFSO = Server.CreateObject("Scripting.FileSystemObject") Set ObjTS = ObjFSO.OpenTextFile(data_file) Do Until ObjTS.AtEndofStream Arr = ObjTS.Readline Arr1 = split(Arr,"::") tanka = FormatCurrency(Arr1(1)) goukei = Arr1(1) * Arr1(2) Loop ObjTS.Close Set ObjTS = Nothing Set ObjFSO = Nothing ================================ この、変数:goukeiを全て加算して、 最後に見積合計を出したいんですが、 これを出すには Redim Preserve goukei() のような、動的配列を使うのですか? それとも使わなくても出来るものなんでしょうか? プロの方にとっては、簡単すぎる質問でしょうが、 分かる方、ぜひご教授お願いします。

  • (VBA) 配列の文字列を昇順で並べ替えたい

    タイトルの通り、配列に格納したファイル名を昇順に並べ替えたいのですが、期待通りに動作しません。 内部コード(ユニコード?)順には並んでいるようですが、エクスプローラの名前順と同等にはなりません。 どのようにしたら、配列のファイル名をエクスプローラと同じように並べ替えできますか? (テストに使用したコード) Public Sub Test1() Dim FileNames() As String Dim WSH As Object Dim MyPathName As String Dim MyFileName As String Dim i As Integer Dim j As Integer '処理対象フォルダを指定 Set WSH = CreateObject("WScript.Shell") MyPathName = WSH.SpecialFolders("MyDocuments") Set WSH = Nothing i = 0 MyFileName = Dir(MyPathName & "\" & "*.*") If MyFileName = "" Then MsgBox "対象ファイルが1つも見つかりません。", , "処理終了" Exit Sub End If 'ファイル一覧を配列に格納 Do Until MyFileName = "" i = i + 1 ReDim Preserve FileNames(1 To i) FileNames(i) = MyFileName MyFileName = Dir Loop '配列を並べ替える For i = 1 To UBound(FileNames) - 1 For j = i To UBound(FileNames) If FileNames(i) > FileNames(j) Then MyFileName = FileNames(i) FileNames(i) = FileNames(j) FileNames(j) = MyFileName End If Next j Next i End Sub ※以降の処理は、Excel で処理するか Access で処理するかまだ決めていません。  (投稿文字数の関係で詳細は省略)

  • VB2005 配列から要素を検索する方法

    VB2005入門者です。 配列の中要素を検索し、一致すれば処理を行うというプログラムを組みたいのですがうまくいきません。 1列目hoge_nameと2列目hoge_noが既に配列に代入してあり 0列目 POINT と1列目hoge_name[AやB]と2列目hoge_no[A00~A03]が一致した場合、3列目のhoge_id(0)からhoge_id(17)に格納したいのです。 hoge.txtは BEBE,A,A00  ,ABC POINT,A,A00  ,ABC POINT,A,A01 ,DEF POINT,B,A02 ,ABC ETC,A,A03   ,GHI POINT,B,A03  ,GHI POINT,A,A03  ,GHI といった感じで、スペースが不特定数混じったカンマ区切りテキストです。 以下で実行すると6行目の3列目でA04を探してしまい、6行目の情報をとってくれません。 hoge_no配列に含まれる文字列から検索し、一致した場合に処理できれば解決するのでは、と思いましたが。Allay.indexOfを試したりしましたがうまくいきませんでした。アドバイス頂きたいのでどなたかよろしくお願いします。 Dim reader1 As New System.IO.StreamReader(geo_name, System.Text.Encoding.Default) Dim i As Long = 0 Dim j As Long Dim answer() As String ReDim answer(0 To 0) Do Until reader1.EndOfStream line = reader1.ReadLine() If (line.Contains("POINT")) Then field = line.Split(",") answer(i) = Trim(field(1)) + "," + (field(2).Trim) + "," + (field(7)) If (field(1).Trim = hoge_name(i) = True And field(2).Trim = hoge_no(i)) = True Then hoge_id(i) = field(7) i = i + 1 ReDim Preserve answer(0 To i) ReDim Preserve hoge_id(0 To i) End If End If Loop For j = 0 To UBound(answer, 1) - 1 reader.Close() Next