更新しました
配列に関して VBScript が他の言語とどのように比較されるかに関心がある場合、 foreach ループ、特にループされたコレクション内の "For Each" によって配信される要素の位置に関する情報を取得することについて、次のような質問を投げかけます。
VBScript は、配列、foreach ループ、特にループされたコレクション内の「For Each」によって提供される要素の位置に関する情報の取得に関して、他の言語と比べてどうですか?
それなら、短い答えはずっと前に利用可能だったでしょう:
foreach ループ コンストラクトは次のことを実現できます
- ポインタ (メモリ アドレス) - たとえば C/C++ のように。次に、ポインターを逆参照して、変更することもできる要素を取得する必要があります。位置情報はポインタ演算で取得可能)
- 参照 (エイリアス) (たとえば、Perl が行うように、変更は可能ですが、明らかに位置の計算はできません (要素にそのような情報が誤って含まれていない限り))
- コピー (たとえば、Python や VBScript のように、メタ情報の変更も取得もできません (AutomatedChaos や AnthonyWJones のような親切で賢い魂が、ループ変数を DIV に送信して C/C++ に似たソリューションを実装するために熱心に取り組んでいない限り) MODs resp. プレーン/必須データ値をメタ情報で拡張できるようにするクラスを設計する)
私の回答の残りの部分は無視しても問題ありません。議論の文脈を提供する次のテキストを削除したくありません。
問題は、それまで対処できません。
(1) armen は、現実世界の問題のコンテキストを現実世界の用語で説明します - 配列がどこから来るのか、可能な次元数、次元構造 (行/列/...) を決定するもの、実行する必要がある操作For Each ループでは、これらの操作でインデックスが重要な理由/方法
(2) すべての貢献者は、ディメンションのセレクターを正しく取得します。
For i = LBound(myArr, 1) to UBound(myArr, 1)
For j = LBound(myArr, 1) to UBound(myArr, 1)
またはそのバリエーションは明らかに間違っている/誤解を招くものです。1 行の 1 を 2 に置き換えないと、コードがどのような行/列構造を意図しているのかが明確になりません。
より建設的な方法で貢献したいと思っていることを証明するために、任意の配列の次元 (の数) を取得する関数を投入します。
Function getDimensions(aVBS)
Dim d : d = 0
If IsArray(aVBS) Then
For d = 1 To 60
On Error Resume Next
UBound aVBS, d + 1
If Err.Number Then Exit For
On Error GoTo 0
Next
End If
getDimensions = d
End Function ' getDimensions
( M. Harris のコードとVBScript Docs の情報に基づく)
更新: まだ解決策ではありませんが、検討の余地があります
アーメン (これまで) は彼の問題の本当の話を提供していなかったので、私は架空のものを提供しようとします (行と列、およびあなたが 3 次元のものと呼ぶものに文脈を与えるために):
魔法のプログラミングを教える学校、ホグモンドがあるとしましょう。VBScript は簡単です (ただし、犬小屋のようなものです)。そのため、テストは 3 つしかなく、学生は学期の途中で入学できます (すべてのペニーがカウントされます)。JScript はより難しいため、フルコースを受講する必要があり、学期中に生徒の数が多い場合は追加のテストがスケジュールされる可能性があります。F# はより複雑であるため、各テストは複数の基準で判断する必要があり、そのうちのいくつかは期間中に合意される可能性があります (教師はまだ学習中です)。C# は非常に「優れた」言語であるため、テストは 1 つしかありません。
そのため、学期の終わりに校長である Bill 'Sauron' Stumblegates 氏は、シートを含む .xls を持っています。
(ドリーンは学期の最後の週に受け入れられました)とシート:
(安心のために、120 の追加テストが隠されています); F# の結果は .txt ファイルに保存されます。
# Results of the F# tests
# 2 (fixed) students, 3 (fixed) test,
# 4>5 (dynamic) criteria for each test
Students Ann Bill
Test TA TB TC TA TB TC
Criteria
CA 1 2 3 4 5 6
CB 7 8 9 10 11 12
CC 13 14 15 16 17 18
CD 19 20 21 22 23 24
# CE 25 26 27 28 29 30
(エクセルで3次元以上のデータを扱うことについて何も知らないので)。
これで、考えるコンテキストができました
- データ: Mary が eval テストで 9 点を獲得したことは重要ですが、その情報が行 5 または 96 に格納されているかどうかは、データ固有のプロパティではありません [つまり、(それ自体が印象的です: 印象的な) (必須) データと (n 任意の) 構造内の位置に関する (偶然の) 情報を組み合わせたオブジェクトを作成する AutomatedChaos のアイデア]
- 処理: 一部の計算 (特にデータセット全体を含む計算) は、行や列に関係なく実行できます (たとえば、すべてのスコアの平均)。再構築/並べ替えが必要な場合もあります (たとえば、すべてのスコアの中央値)。多くの計算 - データの選択/グループ化/サブセットを含むすべて - は、データ項目の位置に関する詳細な知識なしでは実行できません。しかし、armen は処理にまったく関心がない可能性があります。おそらく、彼がインデックスを必要とするのは、要素を表示しながら要素を識別することだけです。[したがって、「Excel/データベースが処理を行うべきではありませんか?」、「読者は 'D5: 9' で満足するでしょうか、それとも 'Mary/eval: 9' を見たいと思うでしょうか?そのような情報は、AutomatedChaos のクラスのより良い候補になるでしょうか?","
- 構造/レイアウト: データを行と列に配置する方法の選択は、利便性 (垂直スクロールが推奨される)、実用的な考慮事項 (「最後に」新しいデータを追加する)、および技術的な理由 (VBScript の「ReDim Preserve」が大きくなる可能性がある) によって決定されます。 (動的)配列は最後の次元のみ)-特定のコンテキスト/タスクに適した各レイアウトには、他の状況(または最初のコンテキスト)でより優れた他の多くの構造があります。確かに「インデクサーの自然な順序」はありません。
現在、ほとんどのプログラマーは、ストーリーを読むことよりもコードを書くこと/コードを書くことを愛しています (コードについて考えたり、計画したり、設計したりすることよりも、コードを書くことの方が好きな場合もあります)。 one-fits-all-dimensions 'For Each' 戦略は、以下に対処する必要があります。
Excel シートからデータを切り取ることができる 2 つの関数があるとします。
Function getXlsRange(sSheet, sRange)
Dim oX : Set oX = CreateObject("Excel.Application")
Dim oW : Set oW = oX.Workbooks.Open(resolvePath("..\data\hogmond.xls"))
getXlsRange = oW.Sheets(sSheet).Range(sRange).Value
oW.Close
oX.Quit
End Function ' getXlsRange
Function getAdoRows(sSQL)
Dim oX : Set oX = CreateObject("ADODB.Connection")
oX.open Join(Array( _
"Provider=Microsoft.Jet.OLEDB.4.0" _
, "Data Source=" & resolvePath("..\data\hogmond.xls") _
, "Extended Properties=""" _
& Join(Array( _
"Excel 8.0" _
, "HDR=No" _
, "IMEX=1" _
), ";" ) _
& """" _
), ";")
getAdoRows = oX.Execute(sSQL).GetRows()
oX.Close
End Function ' getAdoRows
(独自の resolvePath() 関数をロールするか、ファイル仕様をハードコーディングします)
および表示サブ(ループカウンター変数を導入するためにアーメンの非常に良いアイデアを使用します):
Sub showAFE(sTitle, aX)
Dim i, e
WScript.Echo "For Each:", sTitle
WScript.Echo "type:", VarType(aX), TypeName(aX)
WScript.Echo "dims:", getDimensions(aX)
WScript.Echo "lb :", LBound(aX, 1), LBound(aX, 2)
WScript.Echo "ub :", UBound(aX, 1), UBound(aX, 2)
WScript.Echo "s :", UBound(aX, 1) - LBound(aX, 1) + 1 _
, UBound(aX, 2) - LBound(aX, 2) + 1
i = 0
For Each e In aX
WScript.Echo i & ":", e
i = i + 1
Next
End Sub ' showAFE
次のようなコードを使用できます
showAFE "VTA according to XlsRange:", getXlsRange("VTA", "B3:D4")
showAFE "VTA according to AdoRows:", getAdoRows("SELECT * FROM [VTA$B3:D4]")
週末の驚きを得るには:
For Each: VTA according to XlsRange:
type: 8204 Variant()
dims: 2
lb : 1 1
ub : 2 3
s : 2 3
0: 1
1: 2
2: 3
3: 4
4: 5
5: 6
For Each: VTA according to AdoRows:
type: 8204 Variant()
dims: 2
lb : 0 0
ub : 2 1
s : 3 2
0: 1
1: 3
2: 5
3: 2
4: 4
5: 6
そして絶望:
- Mr. Stumblegates の型システムは、これら 2 つの配列が非常に異なる性質を持っているという事実を隠しています (固定配列と動的配列の違いも無視されています)。
- ゼロベースである限り、VBScript ですべての種類の配列を作成できます (Range-born 配列を作成および/または再構築し、それらの (偶発的な!) 1 ベース性を維持する可能性はありません)。
- 2 つの異なる方法で (必然的に) 1 つのレイアウトで 1 つのデータ セットを取得すると、2 つの異なる構造を持つデータが配信されます。
- 「For Each」にデータを列挙するように依頼すると、得られるシーケンスは反復子によって決定され、予測できません (確認/実験する必要があります)。(イテレータの自由/役割を強調することは、AutomatedChaos の最初の回答の 1 つのナゲットです)
[衒学的な批判に興味がない/耐えられない場合は、これを読まないでください:
これは、AnthonyWJones の貢献よりも優れたスコアを持っています。これは、.ArrayList への参照のために、質問も回答も認めていない少なくとも 1 人が賛成票を投じているためです。 ArrayList を多次元にする方法 (つまり、al(1,2,3) に相当するものでアクセス可能)。はい、「IEnumerable」(純粋な .NET の概念) と「マルチスレッド」は印象的なキーワードであり、「オンザフライ」の変更を反映する「ライブ」コレクション (oFolder.Files など) がありますが、(シングル!) スレッドの量はありません。ループ中に謙虚な VBScript 配列を変更できます - Mr. Stumblegates は厳しいマスターです:
Dim a : a = Array(1, 2, 3)
Dim e
WScript.Stdout.WriteLine "no problem looping over (" & Join(a, ", ") & ")"
For Each e In a
WScript.Stdout.Write " " & e
Next
ReDim Preserve a(UBound(a) + 1)
a(UBound(a)) = 4
WScript.Stdout.WriteLine
WScript.Stdout.WriteLine "no problem growing the (dynamic) array (" & Join(a, ", ") & ")"
WScript.Stdout.WriteLine "trying to grow in loop"
For Each e In a
WScript.Stdout.Write " " & e
If e = 3 Then
On Error Resume Next
ReDim Preserve a(UBound(a) + 1)
If Err.Number Then WScript.Stdout.Write " " & Err.Description
On Error GoTo 0
a(UBound(a)) = 5
End If
Next
WScript.Stdout.WriteLine
出力:
no problem looping over (1, 2, 3)
1 2 3
no problem growing the (dynamic) array (1, 2, 3, 4)
trying to grow in loop
1 2 3 This array is fixed or temporarily locked 5
ブランケット ステートメントの別の詳細: 優れたプログラマーでさえ間違いを犯します。特に彼らが助けたいと思っている場合は、好ましくない条件下で作業する必要があります (Stumblegates 氏は、広範なテストなしでは VBScript コードを使用/公開できないようにするために最善を尽くしました)。 )、仕事とライブ、またはただの悪い瞬間があります。
ただし、これは、一部のコードフラグメント/ステートメントが役に立たないか、投票のために問題の解決策を見つけたと考えている SO 読者にとって危険でさえあるという事実を変えるものではありません。コード/テキストの品質は、コンテンツだけの本質的な特性であり、誰がそれを書いたかは単なる偶然です。しかし、「ジョン・ドウのコード」が次のような行を参照する自然な方法であるコンテキストで「客観的」になる方法
for i = 0 to ubound(myArr)
for y = 0 to ubound(myArr, 1)
[UBound(a) and UBound(a, 1) are synonyms, so this will create havoc as soon
as the UBounds of the different dimensions are not (accidentially) the same]
コンテンツに対する投票は、個人の評判に基づいて集計されますか? (評判システムなしで何百万もの回答をリストしますか?ポイントなしで貢献に費やす時間/労力を減らすでしょうか?そうではないことを願っています/推測しますが、私も人間です。)
したがって、getDimensions() 関数で 60 の制限を修正するまで、(少なくとも) この手の込んだものに反対票を投じることをお勧めします。私の気持ちを傷つけることはできません。私がやったのはドキュメントに頼ることだけだったので、私は無責任だと思います:
配列変数の次元。最大 60 個の多次元を宣言できます。
(私が少し恥ずかしいのは、他の人のコードの 999 や 60000 を見たとき、私が優越感を持っていたことです。ミスター・スタンブルゲイツで、しかしチェックしてください:
Dim nDim
For Each nDim In Array(3, 59, 60, 62, 64, 65, 70)
ReDim aDim(nDim)
Dim sDim : sDim = "ReDim a" & nDim & "(" & Mid(Join(aDim, ",0"), 2) & ")"
WScript.Echo sDim
On Error Resume Next
Execute sDim
If Err.Number Then WScript.Echo Err.Description
On Error GoTo 0
Next
出力:
ReDim a3(0,0,0)
...
ReDim a64(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0)
ReDim a65(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
Subscript out of range
...
)
破壊モードで結論付けないでください: 「インデクサーを指定するためにネストされたループで使用される境界 (特に異なる範囲の境界)」というトピックに関する私のハープが、魔法のようにここにある多くのコード行を近い将来に変更することを願っています-それとも私たち全員がホグモンドの学生ではありませんか?
]