Rambler's Top100
Форум: MS ACCESSVBVBA MS OfficeMS SQL server
Новые сообщения: 0000

Форум: MS ACCESS

Вопросы связанные с MS ACCESS

Обновить визитку
Участники «Online»
Все участники

 
 

Доброго времени суток, Посетитель!

вид форума:
Линейный форум Структурный форум

тема: Массивы
 
 автор: Lukas   (07.09.2010 в 18:16)   личное сообщение
 
 

Задачка, однако:
Имеется двухмерный массив varArr в переменной As Variant.
Необходимо получить одномерный массив strArr As String(As Variant),
в котором будут данные второго измерения двухмерного массива varArr.

Циклом поэлементно очень долго.
Как бы "скопом" это осуществить, причем очень быстро?

  Ответить  
 
 автор: kot_k_k   (07.09.2010 в 20:45)   личное сообщение
 
 

двухмерный массив - матрица - n x m.
если m - не заполнен до конца - тебе нужно кол-во столбцов в конкретной строке.

правильно понял???

  Ответить  
 
 автор: Lukas   (08.09.2010 в 01:13)   личное сообщение
 
 

Нет.
Мне нужно из двухмерного массива varArr(0, n) сделать одномерный strArr(n).

  Ответить  
 
 автор: Дядя Федор   (08.09.2010 в 10:02)   личное сообщение
 
 


Мне нужно из двухмерного массива varArr(0, n) сделать одномерный strArr(n).


varArr(0, n) - получается фактически одномерный?
Тогда в твоем примере
CopyMemory varArr(0,0), lngArr(0), Len(lngArr(0)) * 256
Нет?

  Ответить  
 
 автор: Lukas   (08.09.2010 в 12:15)   личное сообщение
 
 

Неа.
Эту функцию можно использовать для массивов любого типа, за исключением строк переменной длины, объектов и пользовательских типов, их содержащих.

  Ответить  
 
 автор: Анатолий (Киев)   (08.09.2010 в 12:24)   личное сообщение
 
 


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. Вы любите эксперименты - попробуйте.

  Ответить  
 
 автор: Дядя Федор   (08.09.2010 в 12:57)   личное сообщение
 
 

Это только принцип. Можно оба массива сделать вариант.
Но мы не знаем, что дальше с ним намеревается делать автор.
Странная задача....

  Ответить  
 
 автор: Lukas   (08.09.2010 в 14:23)   личное сообщение
 
 

Не получилось:
Хоть массив и объявлен varArr() As Variant, после заполнения массива
TypeName(varArr(0, 1)) дает String (естественно переменной длины)

  Ответить  
 
 автор: Дядя Федор   (08.09.2010 в 14:52)   личное сообщение
 
 

max Len посчитать?

  Ответить  
 
 автор: Lukas   (08.09.2010 в 14:55)   личное сообщение
 
 

Чего, зачем и как?

  Ответить  
 
 автор: Анатолий (Киев)   (08.09.2010 в 16:16)   личное сообщение
 
 

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

  Ответить  
 
 автор: Дядя Федор   (08.09.2010 в 16:38)   личное сообщение
 
 

  Ответить  
 
 автор: Lukas   (08.09.2010 в 17:43)   личное сообщение
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 вызов, на третий... всегда по-разному)

  Ответить  
 
 автор: Анатолий (Киев)   (18.09.2010 в 19:01)   личное сообщение
 
 

Lukas, у ADODB.Recordset есть метод GetString. Если ваш запрос возвращает только одно поле, то получить массив значений можно с помощью этого метода и функции Split.
Будет ли это быстрее, чем с массивом - не знаю. Это уже ваша фишка.

  Ответить  
 
 автор: Lukas   (18.09.2010 в 19:43)   личное сообщение
 
 

Спасибо, Анатолий.
Я проверял.
Сам по себе метод GetString медленный.
(Медленнее открытия рекордсета и сборки итоговой строки циклом по рекордсету)
Вся затея была как раз уйти от GetString и от цикла.

Можно открывать рекордсет и собирать итоговую строку циклом по рекордсету.
Можно получить двухмерный массив методом GetRows и собирать итоговую строку циклом по массиву.
Я хотел задействовать Join, она собирает строку в 2 раза быстрее(условно) циклов.
Но ей нужен одномерный массив.

Впрочем, задача скорее академическая, чем практическая,
потому как выигрыш в скорости, если и будет, то растворится на фоне затрат на открытие рекордсета.

  Ответить  
 
 автор: Анатолий (Киев)   (07.09.2010 в 21:03)   личное сообщение
 
 


Циклом поэлементно очень долго.


Убедите меня в этом.

  Ответить  
 
 автор: Lukas   (08.09.2010 в 01:00)   личное сообщение
 
 

Попробую,
на примере одномерных массивов:

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 

  Ответить  
 
 автор: Lukas   (08.09.2010 в 01:06)   личное сообщение
 
 


Циклом поэлементно очень долго.


Здесь имелось в виду следующее:
Долго для решения конкретной задачи.
Понятно, что "на глаз" разовое выполнение незаметно.

  Ответить  
HiProg.com - Технологии программирования
Rambler's Top100 TopList