|
|
|
| Задачка, однако:
Имеется двухмерный массив varArr в переменной As Variant.
Необходимо получить одномерный массив strArr As String(As Variant),
в котором будут данные второго измерения двухмерного массива varArr.
Циклом поэлементно очень долго.
Как бы "скопом" это осуществить, причем очень быстро?
| |
|
| |
|
|
|
| двухмерный массив - матрица - n x m.
если m - не заполнен до конца - тебе нужно кол-во столбцов в конкретной строке.
правильно понял??? | |
|
| |
|
|
|
| Нет.
Мне нужно из двухмерного массива varArr(0, n) сделать одномерный strArr(n). | |
|
| |
|
|
|
|
Мне нужно из двухмерного массива varArr(0, n) сделать одномерный strArr(n).
|
varArr(0, n) - получается фактически одномерный?
Тогда в твоем примере
CopyMemory varArr(0,0), lngArr(0), Len(lngArr(0)) * 256
Нет? | |
|
| |
|
|
|
| Неа.
Эту функцию можно использовать для массивов любого типа, за исключением строк переменной длины, объектов и пользовательских типов, их содержащих. | |
|
| |
|
|
|
|
CopyMemory varArr(0,0), lngArr(0), Len(lngArr(0)) * 256
Нет?
| Нет.
При копировании массива в массив их элементы должны иметь одинаковый тип. У вас же массив элементов типа Variant (16 байт) копируется в массив типа Long (4 байта, хотя у автора тип String, но это тоже 4 байта). И что вы там накопируете?
Выражение Len(lngArr(0)) действительно вернет 4 байта (размер переменной Long), но для переменных типа Variant и String она в данном контексте не годится. Надежнее использовать UBound(), вернее: UBound(arr) - LBound(arr) + 1.
Lukas., а вот копирование блока из двухмерного массива с элементами типа Variant в такой же одномерный массив может получиться. Только не знаю, есть ли разница, если массив внутри переменной Variant. Вы любите эксперименты - попробуйте. | |
|
| |
|
|
|
| Это только принцип. Можно оба массива сделать вариант.
Но мы не знаем, что дальше с ним намеревается делать автор.
Странная задача.... | |
|
| |
|
|
|
| Не получилось:
Хоть массив и объявлен varArr() As Variant, после заполнения массива
TypeName(varArr(0, 1)) дает String (естественно переменной длины)
| |
|
| |
|
|
|
|
| Чего, зачем и как? | |
|
| |
|
|
|
| Lukas, что вы пробовали и что у вас не получилось?
То, что TypeName(varArr(0, 1)) дает String - так и должно быть, но, видимо, вы не знаете, как VB/VBA работает со строками. Как, по-вашему в переменной Variant (16 байт) может вместиться строка в 1000 символов или, например, Recordset?
На самом деле переменная String предсттавляет из себя значение Long, в котором указан адрес начала строки.
Попробуйте следующий тест:
Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)
Sub TestCopyArrays()
Dim Arr1(1 To 2) As Variant, Arr2(1 To 2) As Variant
Arr1(1) = "Первый элемент массива"
Arr1(2) = "Второй элемент массива"
Call CopyMemory(lpvDest:=Arr2(1), lpvSource:=Arr1(1), cbCopy:=UBound(Arr1) * 16)
Debug.Print Arr2(1)
Debug.Print Arr2(2)
Dim Arr3(1 To 2, 1 To 2) As Variant
Arr3(1, 2) = "Первый элемент второй размерности массива"
Arr3(2, 2) = "Второй элемент второй размерности массива"
Call CopyMemory(lpvDest:=Arr2(1), lpvSource:=Arr3(1, 2), cbCopy:=UBound(Arr1) * 16)
Debug.Print Arr2(1)
Debug.Print Arr2(2)
Dim v As Variant
Arr3(1, 2) = "Первый элемент второй размерности массива в переменной Variant"
Arr3(2, 2) = "Второй элемент второй размерности массива в переменной Variant"
v = Arr3
Call CopyMemory(lpvDest:=Arr2(1), lpvSource:=v(1, 2), cbCopy:=UBound(Arr1) * 16)
Debug.Print Arr2(1)
Debug.Print Arr2(2)
End Sub
|
| |
|
| |
|
|
2 Кб. |
|
| Спасибо, Анатолий.
Что хранят переменные типа Object, String и Variant я примерно догадываюсь.
Пример, на основе вашего:
Sub TestCopyArrays3()
Dim Arr1(1 To 2) As Variant
Dim Arr3(1 To 2, 1 To 2) As Variant
Arr3(1, 1) = "Первый элемент первой размерности массива"
Arr3(1, 2) = "Второй элемент первой размерности массива"
Arr3(2, 1) = "Первый элемент второй размерности массива"
Arr3(2, 2) = "Второй элемент второй размерности массива"
Call CopyMemory(lpvDest:=Arr1(1), lpvSource:=Arr3(1, 1), cbCopy:=UBound(Arr1) * 16)
Debug.Print Arr1(1)
Debug.Print Arr1(2)
End Sub
|
Дает результат:
Первый элемент первой размерности массива
Первый элемент второй размерности массива
|
То есть, мне возвращаются значения "построчно", а не "по столбцу" как мне требуется.
Однако, для массива (0, n) это не будет иметь значения.
Sub TestCopyArrays2()
Dim Arr3(0, 1 To 2) As Variant
Dim Arr1() As Variant
ReDim Arr1(1 To 2)
Arr3(0, 1) = "Первый элемент первой размерности массива"
Arr3(0, 2) = "Второй элемент первой размерности массива"
Call CopyMemory(lpvDest:=Arr1(1), lpvSource:=Arr3(0, 1), cbCopy:=UBound(Arr1) * 16)
Debug.Print Arr1(1)
Debug.Print Arr1(2)
End Sub
|
Результат:
Первый элемент первой размерности массива
Второй элемент первой размерности массива
|
То есть, в принципе, то что мне нужно.
Вот то, над чем я веселюсь:
Public Function Test5()
Dim varArr() As Variant, strArr() As Variant
With CurrentDb.OpenRecordset("SELECT Item From tbl21 WHERE ID<7", dbOpenSnapshot)'6 записей
.MoveLast: .MoveFirst
varArr = .GetRows(.recordCount)
ReDim strArr(1 To .recordCount)
.Close
End With
CopyMemory strArr(1), varArr(0, 0), 16 * UBound(strArr)
Debug.Print strArr(1); strArr(2); strArr(3); strArr(4); strArr(5); strArr(6)
End Function
|
Примерный результат =>
Причем, при первом вызове всегда дает правильный результат,
а на последующих либо врет - либо вылет в аут всего Access-а.
(на 2 вызов, на третий... всегда по-разному) | |
|
| |
|
|
|
| Lukas, у ADODB.Recordset есть метод GetString. Если ваш запрос возвращает только одно поле, то получить массив значений можно с помощью этого метода и функции Split.
Будет ли это быстрее, чем с массивом - не знаю. Это уже ваша фишка. | |
|
| |
|
|
|
| Спасибо, Анатолий.
Я проверял.
Сам по себе метод GetString медленный.
(Медленнее открытия рекордсета и сборки итоговой строки циклом по рекордсету)
Вся затея была как раз уйти от GetString и от цикла.
Можно открывать рекордсет и собирать итоговую строку циклом по рекордсету.
Можно получить двухмерный массив методом GetRows и собирать итоговую строку циклом по массиву.
Я хотел задействовать Join, она собирает строку в 2 раза быстрее(условно) циклов.
Но ей нужен одномерный массив.
Впрочем, задача скорее академическая, чем практическая,
потому как выигрыш в скорости, если и будет, то растворится на фоне затрат на открытие рекордсета. | |
|
| |
|
|
|
|
Циклом поэлементно очень долго.
|
Убедите меня в этом. | |
|
| |
|
|
|
| Попробую,
на примере одномерных массивов:
Private lngArr(0 To 255) As Long
Private resArr(0 To 255) As Long
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _
lpvDest As Any, lpvSource As Any, ByVal cbCopy As Long)
Public Declare Function apiTimeGetTime Lib "winmm.dll" Alias "timeGetTime" () As Long
Private Function Test1()
CopyMemory resArr(0), lngArr(0), Len(lngArr(0)) * 256
End Function
Private Function Test2()
Dim i As Integer
For i = 0 To 255
resArr(i) = lngArr(i)
Next i
End Function
Public Sub Test3()
Dim start As Long
Dim j As Long, i As Long
Const Z As Long = 20000
For i = 0 To 5000000 'разогрев процессора
j = i
Next i
For i = 1 To 10
DoEvents
start = apiTimeGetTime
For j = 0 To Z
Test1
Next j
Debug.Print i, "Test1", apiTimeGetTime - start
DoEvents
start = apiTimeGetTime
For j = 0 To Z
Test2
Next j
Debug.Print i, "Test2", apiTimeGetTime - start
Next i
End Sub
|
Результаты:
1 Test1 0
1 Test2 593
2 Test1 16
2 Test2 578
3 Test1 16
3 Test2 578
4 Test1 16
4 Test2 578
5 Test1 15
5 Test2 578
6 Test1 16
6 Test2 563
7 Test1 15
7 Test2 469
8 Test1 16
8 Test2 515
9 Test1 16
9 Test2 578
10 Test1 16
10 Test2 578
|
| |
|
| |
|
|
|
|
Циклом поэлементно очень долго.
|
Здесь имелось в виду следующее:
Долго для решения конкретной задачи.
Понятно, что "на глаз" разовое выполнение незаметно. | |
|
| |