更新
如果有人对 VBScript 与其他语言在数组、foreach 循环方面的比较,特别是获取有关“For Each”在循环中传递的元素位置的信息感兴趣的人,会提出如下问题:
VBScript 在数组、foreach 循环方面与其他语言相比如何,尤其是在循环集合中获取有关“For Each”传递的元素位置的信息?
那么很久以前就会有一个简短的答案:
foreach 循环构造可以提供
- 一个指针(内存地址)——就像 C/C++ 那样;然后你必须取消引用指针以获取你甚至可以更改的元素;位置信息可通过指针算术获得)
- 引用(别名)(例如 Perl 所做的;允许修改,但显然不计算位置(除非元素意外包含此类信息))
- 一个副本(例如 Python 或 VBScript 所做的;修改或检索元信息都是不可能的(除非像 AutomatedChaos 或 AnthonyWJones 之类的善良和聪明的灵魂通过向 DIV 提交循环变量和MODs 分别设计一个允许使用元信息增加普通/基本数据值的类)
您可以放心地忽略我的其余答案;我只是不想删除以下为讨论提供一些背景的文本。
问题无法解决,直到
(1) armen 用现实世界的术语描述现实世界问题的上下文——数组从哪里来,有多少维是可能的,什么决定了维度结构(行/列/...),必须进行哪些操作在 For Each 循环中,为什么/如何索引对这些操作很重要
(2) 所有贡献者都获得了正确的维度选择器:
For i = LBound(myArr, 1) to UBound(myArr, 1)
For j = LBound(myArr, 1) to UBound(myArr, 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 的信息)
更新:仍然不是解决方案,但值得深思
由于 armen(到目前为止)没有提供他的问题的真实故事,我试着给你一个虚构的故事(给行和列加上上下文,以及你在第三维中可能称之为的东西):
假设有一所学校 - Hogmond - 教授魔法编程。VBScript 很容易(但在狗窝里),所以只有三个测试,学生在中期被录取(每一分钱都很重要)。JScript 更难,所以你必须完成完整的课程,如果学生证明很厚,可能会在学期期间安排额外的测试。F#比较复杂,所以每次考试都要根据多个标准来判断,其中一些可能会在学期中达成一致(老师还在学习中)。C# 是如此“好”的语言,以至于只有一个测试。
因此,在学期结束时,校长 - Bill 'Sauron' Stumblegates 先生 - 有一个 .xls,其中包含一张表:
(Doreen 在学期的最后一周被录取)和一张纸:
(为了您的安心,隐藏了 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
(因为我对在 Excel 中处理三维数据一无所知)。
现在我们有一个上下文要考虑
- 数据:玛丽在 eval 测试中得了 9 分很重要,但该信息是否存储在第 5 行或第 96 行并不是数据的固有属性[暗示您在开始之前应该三思而后行(单独考虑:令人印象深刻)AutomatedChaos 的想法,用于创建将(基本)数据和(偶然)信息组合在(n 任意)结构中的位置的对象。]
- 处理:一些计算——尤其是那些涉及整个数据集的计算——可以在不考虑行或列的情况下完成(例如所有分数的平均值);有些甚至可能需要重组/重新排序(例如所有分数的中位数);许多计算 - 所有涉及数据的选择/分组/子集 - 如果没有对数据项位置的深入了解,就无法完成。然而,armen 可能对处理根本不感兴趣——也许他所需要的只是在显示元素时识别元素。[因此,推测诸如“Excel/数据库不应该进行处理吗?”、“读者会满足于'D5:9'还是他希望看到'Mary/eval:9'这样的问题是徒劳的-和这样的信息会更适合 AutomatedChaos 的课程吗?", "
- 结构/布局:如何将数据放入行和列的选择取决于便利性(首选垂直滚动)、实际考虑因素(在“末尾”附加新数据)和技术原因(VBScript 的“ReDim Preserve”可以增长(仅在最后一维中的(动态)数组) - 因此对于给定上下文/任务有意义的每个布局,有许多其他结构在其他情况下(甚至第一个上下文)更好。当然,没有“索引器的自然顺序”。
现在大多数程序员更喜欢编写/编写代码而不是阅读故事(有些人更喜欢思考/计划/设计代码),所以这里只是一个例子来展示我们的白日梦/魔法有哪些不同的野兽(数组,“迭代器”)千篇一律的“For Each”策略必须应对:
给定两个可让您从 Excel 工作表中剪切数据的函数:
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() 函数或硬编码文件规范)
和一个显示子(使用armen的非常好的想法来引入一个循环计数器变量):
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
和绝望:
- Stumblegates 先生的类型系统隐藏了这两个数组具有非常不同的性质的事实(并且也忽略了固定数组和动态数组之间的区别)
- 您可以在 VBScript 中创建各种数组,只要它们是从零开始的(没有机会创建和/或重组 Range-born 数组并保持它们(偶然!)从一开始)
- 通过两种不同的方法获取具有(必然)一种布局的一组数据将提供具有两种不同结构的数据
- 如果您要求“For Each”枚举数据,那么您获得的序列由迭代器确定并且不可预测(您必须检查/实验)。(强调迭代器的自由/角色是 AutomatedChaos 的第一个答案中的一个重点)
[不要读这个,如果你不感兴趣/不能忍受迂腐的诽谤:
它仍然比 AnthonyWJones 的贡献得分更高,因为至少有一个人承认既没有回答也没有回答问题,因为对 .ArrayList 的引用 - 这与 armen 的问题完全不相关,因为没有使 ArrayList 多维的方法(即:可通过等价于 al(1,2,3) 访问)。是的,“IEnumerable”(纯 .NET 概念)和“多线程”是令人印象深刻的关键字,并且有反映“动态”修改的“实时”集合(例如 oFolder.Files),但没有多少(单!)线程将让您在循环时修改一个不起眼的 VBScript 数组 - 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 读者来说是无用的,甚至是危险的,因为他们认为他们已经找到了解决问题的方法。代码/文本的质量是内容本身的基本属性,谁写的只是偶然的。但是如何在“Jon Doe 的代码”是指代以下行的自然方式的上下文中“客观”
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 时,我有一种优越感——正如我所说:我也只是人类;并且:不要相信你在 Stumblegates 先生中,但请检查:
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
...
)
不要以破坏性模式得出结论:我仍然希望我对“嵌套循环中用于指定索引器的边界(尤其是不同范围的范围)”主题的反复强调会神奇地导致这里的许多代码行在不久的将来被更改 -或者我们不都是霍格蒙德的学生吗?
]