Настройка строки переменной для получения значений из структурированного массива

#arrays #vb.net #structure

Вопрос:

У меня есть простая проблема в VB.net. Я хочу извлечь данные из массива, объявленного как структура, используя строку для визуализации переменной. Ниже моего кода:

 Module DataAnalisys

Dim InputData(100000) As InputDataStructure
Dim VariablesParameter(6) As VariablesParameterStructure
Dim VariablesGlobal(6) As VariablesDataStrucutre

Structure InputDataStructure
    Dim ID As Integer
    Dim FileId As Integer
    Dim ProductionDate As Date
    Dim ProductionTime As Date
    Dim Shift As Integer
    Dim IPP As Integer
    Dim BPS As Integer
    Dim SerialNumber As String
    Dim Top As Single
    Dim Bottom As Single
    Dim Right As Single
    Dim Left As Single
    Dim OffCutH As Single
    Dim OffCutV As Single
    Dim Row As Byte
    Dim Col As String
    Dim Position As String
    Dim Pack As Integer
    Dim Sheet As Integer
    Dim SheetInPack As Integer
End Structure

Structure VariablesParameterStructure
    Dim NameParameter As String
    Dim Target As Single
    Dim Tolerance As Single
    Dim LowTolerance As Single
    Dim UppTolerance As Single
End Structure

Structure VariablesDataStrucutre
    Dim NameData As String
    Dim Position As String
    Dim N As Long
    Dim Mean As Single
    Dim Difference As Single
    Dim Scrap As Single
    Dim ScrapGreat As Single
    Dim ScrapLess As Single
    Dim SeMean As Single
    Dim StDev As Single
    Dim Min As Single
    Dim Q1 As Single
    Dim Median As Single
    Dim Q3 As Single
    Dim Max As Single
End Structure

Sub RoutineAnalisysGlobal()

    For B As Byte = 0 To VariablesGlobal.Length - 1

        Dim ID As String = VariablesParameter(B).NameParameter
        Dim Target As Single = VariablesParameter(B).Target
        Dim LowTol As Single = VariablesParameter(B).LowTolerance
        Dim UppTol As Single = VariablesParameter(B).UppTolerance

        With VariablesGlobal(B)
            .NameData = ID
            .Position = "All"
            .N = InputData.Length
            .Mean = InputData.Average(Function(f) f.ID)
            .Difference = .Mean - Target
            .ScrapLess = InputData.Count(Function(f) f.ID < LowTol)
            .ScrapGreat = InputData.Count(Function(f) f.ID > UppTol)
            .Q1 = InputData.FirstQuartile(Function(F) F.ID)
            .Min = InputData.Min(Function(f) f.ID)
            .Median = InputdataMedian(Function(f) f.ID)
            .Q3 = InputData.ThirdQuartile(Function(f) f.ID)
            .Max = InputData.Max(Function(f) f.ID)
        End With
    Next

End Sub

End Module
 

Переменные Parameter().Имя: Верхний, Нижний, Правый, Левый, OffCutH и OffCutV.

Код не работает, когда я использую часть (функцию(f) f.ID) так как я хочу использовать функцию для разных элементов структуры входных данных в соответствии с циклом for. Я хочу отобразить f.Сверху f.Снизу f.Справа f.Слева f.OffCutH и f.OffCutV.

Кто-нибудь хочет мне помочь? Я смотрел, как преобразовать идентификатор строки в переменную inputData.?????? структура.

Комментарии:

1. Что именно это должно делать: .Mean = InputData.Average(Function(f) f.ID) ? В нынешнем виде вы получаете среднее значение всех ID значений, что, похоже, не имеет смысла. Я подозреваю, что на самом деле вы хотите получить среднее значение какого-либо другого поля для всех элементов, где ID оно равно некоторому определенному значению. Это правильно? Если это так, то вам нужно сначала позвонить Where , чтобы отфильтровать данные, затем позвонить Average и указать поле, в котором вы хотите получить среднее значение, например .Mean = InputData.Where(Function(f) f.ID = ID).Average(Function(f) f.Top) .

2. @jmcilhinney, возможно, идентификатор вводит в заблуждение, поскольку он присутствует в коде два раза. Я хочу сделать действительно простую вещь, например, просто следующие 6 средних значений: InputData.Average(Function(f) f.Top) - InputData.Average(Function(f) f.Bottom) - InputData.Average(Function(f) f.Right) - InputData.Average(Function(f) f.Left) - InputData.Average(Function(f) f.OffCutHTop) - InputData.Average(Function(f) f.OffCutV) но я не знаю, как перейти в цикл for между f.Top, f.Bottom, f.Right, f.Left, f.OffCutH and f.OffCutV

3. Похоже, вам нужны массивы с 6 элементами, учитывая, что вы говорите об усреднении 6 полей. Если это так, то способ , которым вы создаете свои массивы, например Dim VariablesParameter(6) As VariablesParameterStructure , неверен. Это создает массив из 7 элементов. Вы указываете верхнюю границу при создании массива в VB, которая на 1 меньше длины.

4. У вас также есть некоторые орфографические ошибки в коде. У вас есть «Анализ» вместо «Анализ» в двух местах и «Структура» вместо «Структура» в одном месте. Вы также должны иметь Option Strict Off для этого код для компиляции, что плохо. Почему вы думаете, что использование a Byte в For цикле для индексирования массивов было хорошей идеей? Индексы ЕСТЬ ВСЕГДА Integers . Включите Option Strict On этот проект и сделайте его по умолчанию в параметрах VS для всех проектов и помогите себе перестать писать плохой код.

Ответ №1:

Я думаю, что вы ищете что-то вроде этого:

 Dim functions As Func(Of InputDataStructure, Single)() = {Function(ids) ids.Top,
                                                          Function(ids) ids.Bottom,
                                                          Function(ids) ids.Right,
                                                          Function(ids) ids.Left,
                                                          Function(ids) ids.OffCutH,
                                                          Function(ids) ids.OffCutV}

For i = 0 To VariablesGlobal.GetUpperBound(0)
    With VariablesGlobal(i)
        '...
        .Mean = InputData.Average(functions(i))
        '...
    End With
Next
 

В качестве объяснения, Function(f) f.ID часть этой строки:

 .Mean = InputData.Average(Function(f) f.ID)
 

является лямбда-выражением, т. е. делегатом для анонимного метода. Average Метод , который вы вызываете, требует делегата для метода, имеющего один аргумент типа T , который T соответствует универсальному типу IEnumerable(Of T) , для которого вы его вызываете, и возвращает значение числового типа, например Integer или Single . В вашем случае вы Average призываете InputData , что реализует IEnumerable(Of InputDataStructure) . Ваше лямбда-выражение имеет параметр типа InputDataStructure и возвращает значение an Integer , поэтому оно работает с Average . Average Метод вызывает этот делегат для каждого элемента в списке, получает все числовые результаты, а затем делит их на количество элементов в списке, чтобы получить среднее значение.

Чтобы было понятнее, что там происходит, вы могли бы использовать именованный метод вместо лямбда-выражения. Вы могли бы написать этот метод:

 Private Function GetID(f As InputDataStructure) As Integer
    Return f.ID
End Function
 

а затем измените свой код на этот:

 .Mean = InputData.Average(AddressOf GetID)
 

Надеюсь, вы сможете определить части этого именованного метода, которые соответствуют лямбда-выражению, и части, которые выводятся.

Теперь у вас есть несколько строк кода, в которых используется одно и то же лямбда-выражение, например

 .Min = InputData.Min(Function(f) f.ID)
.Median = InputdataMedian(Function(f) f.ID)
.Q3 = InputData.ThirdQuartile(Function(f) f.ID)
 

для чего-то столь простого принято делать это так, как у вас есть. Однако у вас есть возможность написать лямбда-выражение один раз, присвоить его переменной, а затем использовать эту переменную несколько раз. Ты мог бы сделать это:

 Dim f As Func(Of InputDataStructure, Integer) = Function(ids) ids.ID
 

или это:

 Dim f = Function(ids As InputDataStructure) ids.ID
 

чтобы в конечном итоге переменная f ссылалась на делегат, который принимает аргумент типа InputDataStructure и возвращает значение типа Integer . Затем вы можете использовать ту переменную, в которой ранее использовали несколько лямбда-выражений:

 .Min = InputData.Min(f)
.Median = InputdataMedian(f)
.Q3 = InputData.ThirdQuartile(f)
 

Итак, теперь, когда мы определили, что все, что вам нужно, — это делегат метода, который принимает аргумент правильного типа и возвращает правильный тип, еще раз взгляните на код, который я опубликовал. Сначала он создает массив из шести делегатов, каждый из которых принимает аргумент типа InputDataStructure и возвращает значение типа Single . Затем вы можете использовать элемент этого массива везде, где ожидается такой делегат.

Это означает, что вы можете перебирать этот массив и передавать каждый делегат всем методам расширений, которые ожидают такого делегата, в том числе Average . Если вы получите делегата с индексом 0, то вы получите делегата, который возвращает значение Top свойства, поэтому Average получите среднее Top значение. Если вы используете индекс 5, то вы получите делегат, который возвращает значение OffCutV свойства, поэтому Average получите среднее OffCutV значение. И т.д.

Для записи я рекомендую использовать имена параметров в лямбдах, которые указывают тип. Ты использовал f , но это бессмысленно. Тип параметра InputDataStructure so ids указывает на это.