Обходной путь для перегрузки оператора в VBA - PullRequest
2 голосов
/ 29 марта 2019

Мне нужно создать новый класс в VBA, который может поддерживать некоторые базовые математические операции (Add, Multiply и т. Д.).Перегрузка операторов не разрешена в VBA, поэтому по этой причине я думал о создании следующего интерфейса

INumeric

Public Function Add(ByVal other As INumeric) As INumeric
End Function

Public Function Multiply(ByVal other As INumeric) As INumeric
End Function

Public Function Negative() As INumeric
End Function

'[...] etc

, который определяет реакцию на все операторы, которые мне бы хотелосьиспользовать (+, -, ^, % и т. д.).Возможно, лучше было бы использовать отдельный интерфейс для каждого, или свободный интерфейс типа утки (т. Е. Просто определить .Add метод , как это делает python , нет Implements)

Это можетиспользоваться вместе с классом математики для выполнения функций вместо операторов:

Maths

Option Explicit
'@PredeclaredID

Public Function Add(ByVal first As Variant, ByVal second As Variant) As Variant

    If TypeOf first Is INumeric Then
        On Error GoTo defaultAdd
            Dim numericFirst As INumeric
            Set numericFirst = first
            Set Add = numericFirst.Add(second)

    ElseIf TypeOf second Is INumeric Then
        On Error GoTo defaultAdd
            Dim numericSecond As INumeric
            Set numericSecond = second
            Set Add = numericSecond.Add(first)

    Else
defaultAdd:
        On Error GoTo -1
        On Error GoTo errHandle
            Add = first + second
    End If
    Exit Function

errHandle:
    err.Description = "Arguments couldn't be added :( try implementing INumeric"
    err.Raise 5
End Function

Public Function Negate(ByVal value As Variant) As Variant
    'Similar sort of stuff
End Function

'[...] etc

, а затем в моем коде я могу

Dim result As Variant
Set result = Maths.Add(INumeric1, INumeric2) 'returns INumeric probably
result = Maths.Add(IsNumeric1, IsNumeric2) 'returns IsNumeric probably

, где IsNumeric1 просто представляет некоторое значение с определенным оператором + - например, Long или Double (или даже String; "3" + "4" = "34" ofc!)


Как видите, это очень быстро, особенно если я хочу реализовать более 10 операторов.И в этом отношении остальная часть библиотеки VBA Math - мой текущий подход кажется многословным и не особенно СУХИМЫМ.

Так что мне интересно есть ли какая-нибудь библиотека, которую я могу добавить в свой проект VBA, которая определяет интерфейсы, подобные этим, и использует их в математических или логических уравнениях?Или, говоря по-другому, избегайте вопроса XY; Как я могу создавать пользовательские типы данных, которые можно использовать непосредственно в общих математических выражениях? Я мог бы представить создание классов, которые определяют их собственный ответ на операции, но я открыт для других подходов.


Приложение

FWIW, я создал пример класса реализации (также предварительно объявленного для метода Create (Take;)).Он представляет собой научное измерение, которое имеет значение и неопределенность

Option Explicit
'@PredeclaredID

Implements INumeric

Public value As Double
Private uncertainty As Double

Public Function Take(ByVal apparentValue As Double, Optional ByVal absErr As Double = 0, Optional ByVal relErr As Double = 0) As Measurement
    With New Measurement
        .value = apparentValue
        If absErr <> 0 Then
            .absoluteErr = absErr
        ElseIf relErr <> 0 Then
            .relativeErr = relErr
        Else
            'must be a perfect number, no errors
            .absoluteErr = 0 'default value so no point
        End If
        Set Take = .Self
    End With
End Function

Public Property Get relativeErr() As Double
    relativeErr = Abs(uncertainty / value)
End Property

Public Property Let relativeErr(ByVal relErr As Double)
    uncertainty = relErr * value
End Property

Public Property Get absoluteErr() As Double
    absoluteErr = Abs(uncertainty)
End Property

Public Property Let absoluteErr(ByVal absErr As Double)
    uncertainty = absErr
End Property

Public Property Get Self() As Measurement
    Set Self = Me
End Property

Public Function toString(Optional ByVal formatWithRelErr As Boolean = True) As String
    If formatWithRelErr Then
        toString = value & "±" & Me.relativeErr * 100 & "%"
    Else
        toString = value & "±" & Me.absoluteErr
    End If
End Function

Private Function INumeric_Add(ByVal other As Variant) As INumeric
    If TypeOf other Is Measurement Then
        Set INumeric_Add = Measurement.Take(Me.value + other.value, absErr:=Me.absoluteErr + other.absoluteErr)
    Else
        Set INumeric_Add = Maths.Add(Me, Measurement.Take(other))
        'or Set INumeric_Add = Measurement.Take(Me.value + other, absErr:=Me.absoluteErr)
    End If
End Function

Private Function INumeric_Multiply(ByVal other As Variant) As INumeric
    If TypeOf other Is Measurement Then
        Set INumeric_Multiply = Measurement.Take(Me.value * other.value, relErr:=Me.relativeErr + other.relativeErr)
    Else
        Set INumeric_Multiply = Maths.Multiply(Me, Measurement.Take(other))
    End If
End Function

Private Function INumeric_Negative() As INumeric
    Set INumeric_Negative = Measurement.Take(-Me.value, absErr:=Me.absoluteErr)
End Function

и может быть проверено с помощью этого

Sub test()
    Dim a As Measurement, b As Measurement, c As Integer
    Set a = Measurement.Take(10, absErr:=1)
    Set b = Measurement.Take(15, relErr:=0.2)
    c = 3 'treated equiv to Measurement.Take(3, relErr = 0)

    Debug.Print "a = "; a.toString; " = "; a.toString(False)
    Debug.Print "b = "; b.toString; " = "; b.toString(False)
    Debug.Print "c ="; c

    Dim aPlusB As Measurement
    Set aPlusB = Maths.Add(a, b)
    Debug.Print "a + b = "; aPlusB.toString

    Dim bPlusC As Measurement
    Set bPlusC = Maths.Add(c, b)
    Debug.Print "b + c = "; bPlusC.toString(formatWithRelErr:=False)

    Debug.Print "3.2 + 7.3 ="; Maths.Add(3.2, 7.3)

    On Error Resume Next
        Debug.Print Maths.Add("k", 4)
        If err.Number <> 0 Then Debug.Print "Err:"; err.Number; "- "; err.Description
End Sub

, который выводит это

a = 10±10% = 10±1
b = 15±20% = 15±3
c = 3
a + b = 25±16%
b + c = 18±3
3.2 + 7.3 = 10.5 
Err: 5 - Arguments couldn't be added :( try implementing INumeric

1 Ответ

0 голосов
/ 29 марта 2019

Не уверен, что я нахожусь на той же странице, но вы могли бы сделать что-то вроде этого в clsMaths

Private colNumbersForOperation As Collection

Public Enum enmOperations
    enmSum = 1
    enmMult = 2
    enmDivide = 3
End Enum

Private Sub class_initialize()

    Set colNumbersForOperation = New Collection

End Sub

Public Function Load_Number_To_Collection(varEntry As Variant) As Collection

Dim c As Excel.Range

    If TypeOf varEntry Is Excel.Range Then
        For Each c In varEntry.Cells
            colNumbersForOperation.Add c.Value
        Next c
    Else
        colNumbersForOperation.Add varEntry
    End If

End Function

Public Function OperateOnNumbers(enmOperation As enmOperations) As Double

Dim v As Variant

For Each v In colNumbersForOperation
    Select Case enmOperation
        Case enmSum
            OperateOnNumbers = OperateOnNumbers + v
        Case enmMult
            OperateOnNumbers = IIf(OperateOnNumbers = 0, v, OperateOnNumbers * v)
        Case enmDivide
            OperateOnNumbers = IIf(OperateOnNumbers = 0, v, OperateOnNumbers / v)
        Case Else
    End Select
Next v


End Function

Это можно использовать так:

Dim c As New clsMaths

c.Load_Number_To_Collection 10
c.Load_Number_To_Collection 20
c.Load_Number_To_Collection Range("a1:a10")

Debug.Print c.OperateOnNumbers(enmSum)
Debug.Print c.OperateOnNumbers(enmMult)
Debug.Print c.OperateOnNumbers(enmDivide)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...