Lista de Operadores Matematicos

Iniciado por rigorvzla, 9 Febrero 2021, 18:19 PM

0 Miembros y 1 Visitante están viendo este tema.

rigorvzla

Hola a todos, estoy en un problema, creo una lista de tipo string, donde guardo los simbolos matematicos, (+) (-) (*) (/), quiero usarlos para que hagan su funcion de acuerdo a lo que pido, pero no encuentro como hacerlo.

actualemnte esta asi
  calculo = calculo + Convert.ToInt32(valores[i]);

pero lo necesito asi

for{ calculo = calculo ListaSimbolo[i] Convert.ToInt32(valores[i]);}
espero puedan ayudarme a resolver este problema


Serapis

Los operadores no son variables.
...pero, en .NET puedes sobrecargar los operadores o definir los propios.

Podrías implementar un operador 'Operar(x,y,z)', donde uno de los parámetros fuera precisamente el operador a utilizar (basado en una enumeración), los otros parámetros serían los propios del operador.
en realidad equivaldría a una función donde pasas esos parámetros y allí mediante un 'select case' evalúa la expresión con el operador elegido.

La ventaja de definir e implementar un operador es que se puede usar de la manera concreta que precises.

Mr. NoBody

#2
No pierdas el tiempo ni reinventes la rueda, utiliza un evaluador de expresiones matemáticas. O bien eliges utilizar la función Eval de javascript (mediante el motor de JS de .NET), o bien eliges utilizar la librería de terceros NCalc:


Eval y NCalc son metodologías que implican un tiempo lento de inicialización, pero ambas son robustas, a prueba de fallos. Aquí puedes ver el sofisticado procedimiento de análisis o data parsing y compilación de una expresión matemática con NCalc: https://github.com/ncalc/.../NCalcParser. En general, evaluar expresiones con NCalc es bastante más rápido que con Eval, esto sobre todo se nota al medir el tiempo de ejecución cuando llevamos a cabo muchas operaciones de forma consecutivas en búcle, y en fin, es una solución open-source.

Ejemplo propio en VB.NET para validar sintaxis, y evaluar expresión:

Código (vbnet) [Seleccionar]
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Validates the syntax of an arithmetic expression.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <example> This is a code example.
''' <code language="VB.NET">
''' Dim expr As String = "1 + ABC"
''' Dim isValid As Boolean = ValidateExpression(expr)
''' </code>
''' </example>
''' ----------------------------------------------------------------------------------------------------
''' <returns>
''' <see langword="True"/> if the arithmetic expression does not contain syntax errors,
''' otherwise, <see langword="False"/>.
''' </returns>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Shared Function ValidateExpression(expr As String) As Boolean

    Dim calc As New Global.NCalc.Expression(expr)

    If calc.HasErrors() Then
        Return False
    End If

    Try
        Decimal.TryParse(CStr(calc.Evaluate()), New Decimal)
        Return True

    Catch
        Return False

    End Try

End Function


Código (vbnet) [Seleccionar]
''' ----------------------------------------------------------------------------------------------------
''' <summary>
''' Evaluate and return the result of an arithmetic expression.
''' </summary>
''' ----------------------------------------------------------------------------------------------------
''' <example> This is a code example.
''' <code language="VB.NET">
''' Dim expr As String = "(1 + (2 - 2)) * (100 / 2.5)" ' = 40
''' Dim result As Decimal = EvaluateExpression(expr)
''' </code>
''' </example>
''' ----------------------------------------------------------------------------------------------------
''' <returns>
''' The resulting value.
''' </returns>
''' ----------------------------------------------------------------------------------------------------
''' <exception cref="Global.NCalc.EvaluationException">
''' </exception>
''' ----------------------------------------------------------------------------------------------------
<DebuggerStepThrough>
Public Shared Function EvaluateExpression(expr As String) As Decimal

   Dim calc As New Global.NCalc.Expression(expr)
   Dim result As Decimal

   If calc.HasErrors() Then
       Throw New Global.NCalc.EvaluationException(message:=calc.Error)

   Else
       Try
           result = CDec(calc.Evaluate())
           Return result

       Catch ex As Global.NCalc.EvaluationException
           Throw

       Catch ex As ArgumentException
           Throw New Global.NCalc.EvaluationException(message:=ex.Message)

       Catch ex As Exception
           Throw

       End Try

   End If

End Function


Importante: NCalc no se limita solamente a evaluar expresiones de tipos numéricos, sino que además puede evaluar otros tipos como DateTime y comparaciones de caracteres entre strings, pero las funciones ValidateExpression y EvaluateExpression del código de arriba funcionarán de forma correcta solamente con los tipos simples numéricos, es decir integrales o de punto flotante ( https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-types#built-in-value-types ) ya que es el uso que se le pretende dar.

Si prefieres hacerte el guiso por ti mismo, aquí tienes otras alternativas en C# (ordenados por fecha de antiguedad):

Saludos.

Serapis

La solución que te ofrece Elek... digo el señor Nobody, es muy válida cuando tengas expresiones enteramente textuales, pero si el contenido procede de entresacar valores de alguna parte que vuelcas sobre un array, es preferible el método que te he descrito... también será más rápido.

Te pongo un ejemplo, tu completa, corrige allí donde precises y haz los cambios que necesites...

Primero el código de ejemplo... cuyo resultado arroja 45.
(LaClaseDondeLoPongas hace referencia a una clase donde coloques el código de la siguiente sección)
Código (vbnet) [Seleccionar]

Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
        Dim v() As String, o() As String
        Dim resultado As Single

        ReDim v(0 To 4), o(0 To 4)
        v(4) = "5" : v(3) = "7" : v(2) = "11" : v(1) = "13" : v(0) = "17"
        o(4) = "/" : o(3) = "-" : o(2) = "+" : o(1) = "*" : o(0) = "+"

        resultado = LaClaseDondeLoPongas.Operandos.Totalizar(v, o)
        System.Windows.Forms.MessageBox.Show(resultado.ToString)
    End Sub



Y el código que realiza todo el trabajo:
Código (vbnet) [Seleccionar]

  Public Structure Operandos
        Public Total As Single
        Private Operando As Integer
        Private Operador As Operadores

        Public Enum Operadores
            OPERADOR_SUMA = 0
            OPERADOR_RESTA = 1
            OPERADOR_MULTIPLICA = 2
            OPERADOR_DIVIDE = 3
        End Enum

        Public Shared Function Totalizar(ByRef Valores() As String, ByRef Operadores() As String) As Single
            Dim k As UInt32
            Dim T As New Operandos
            Dim Resul As New Operandos

            ' NOTA: se supone que ambos arrays son igual de largos (el primer valor se considera 0, luego se entiende que el operador para el primer operando es '+').
            ' Procesa un operando de cada vez, con el operador asociado sobre el total acumulado
            '    (si entendi mal deberas corregirlo a tus necesidades)
            For k = 0 To Valores.Length - 1
                With T
                    .Operando = Convert.ToInt32(Valores(k))
                    .Operador = ToOperador(Operadores(k))
                    .Total = Resul.Total
                    Resul = (T = Resul)
                End With
            Next

            Return Resul.Total
        End Function

        Public Shared Operator =(ByVal XY As Operandos, ByVal R As Operandos) As Operandos
            With XY
                Select Case .Operador
                    Case Operadores.OPERADOR_SUMA
                        Try
                            .Total += .Operando
                        Catch ex As Exception
                            System.Windows.Forms.MessageBox.Show(ex.Message)
                        End Try
                    Case Operadores.OPERADOR_RESTA
                        Try
                            .Total -= .Operando
                        Catch ex As Exception
                            System.Windows.Forms.MessageBox.Show(ex.Message)
                        End Try
                    Case Operadores.OPERADOR_MULTIPLICA
                        Try
                            .Total *= .Operando
                        Catch ex As Exception   ' desbordamiento, etc...
                            System.Windows.Forms.MessageBox.Show(ex.Message)
                        End Try
                    Case Operadores.OPERADOR_DIVIDE
                        Try
                            .Total /= .Operando
                        Catch ex As Exception   ' division por 0, etc...
                            System.Windows.Forms.MessageBox.Show(ex.Message)
                        End Try
                End Select

                Return XY
            End With
        End Operator

        ' Declarar un operador '=' exige declarar tambien otro '<>' (aunque no lo usemos).
        Public Shared Operator <>(ByVal XY As Operandos, ByVal R As Operandos) As Operandos
            Return Nothing
        End Operator

        ' Resulta autoexplicativo...
        Private Shared Function ToOperador(ByVal Op As String) As Operadores
            Select Case Op.Trim
                Case "+" : Return Operadores.OPERADOR_SUMA
                Case "-" : Return Operadores.OPERADOR_RESTA
                Case "*" : Return Operadores.OPERADOR_MULTIPLICA
                Case "/" : Return Operadores.OPERADOR_DIVIDE
            End Select
        End Function
  End Structure

rigorvzla

MUCHISIMAS MUCHISIMAS GRACIAS!!! Elektro mucho tiempo sin saber de ti, q bueno estas vivo aun jeje , ya me pondre con ello de inmediato a ver como optimizar el codigo , de todos modos les compartire la solucion que encontre de momento.

            for (int i = 0; i < valores.Length; i++)
            {
                string calc = $"{ListaSimbolos[i]}{valores[i]}";
                if (calc.Contains("+"))
                {
                    calculo = calculo + Convert.ToInt32(calc);
                }
                else if (calc.Contains("-"))
                {
                    calculo = calculo + Convert.ToInt32(calc);
                }
                else if (calc.Contains("*"))
                {
                    string ca = calc.Replace("*", " ").Trim();
                    calculo = Convert.ToInt32(calculo) * Convert.ToInt32(ca);
                }

                else if (calc.Contains("/"))
                {               
                    string ca = calc.Replace("/", " ").Trim();
                    calculo = Convert.ToInt32(calculo) / Convert.ToInt32(ca);
                }
            }


Explico de manera rapida, creo una lista de los signos que pido en su respectivo orden, luego obtengo cada numero y  evaluo el simbolo que tiene cada valor para asi , ejecutar dicha operacion matematica, creanlo o no, me ha servido , pero si , con Ncalc simplifico esto estaria espectacular.

Nuevamente gracias y me pondre con esto. saludos a todos los q me ayudaron, gracias.

Mr. NoBody

#5
Cita de: rigorvzla en 10 Febrero 2021, 18:17 PMles compartire la solucion que encontre de momento.

Si quieres micro-optimizar ese código entonces quizás podrías tratar de aplicar el ejemplo del usuario Serapis ya que ha invertido tiempo en hacerlo y es más completo o sofisticado que la solución actual que aplicaste, donde su ejemplo de usar Select Case / switch es más apropiado (y también más elegante, dicho sea de paso) que utilizar un bloque de condicionales else + contains ya que tienes un set de símbolos o valores fijos ( https://www.geeksforgeeks.org/switch-vs-else/ ). Además deberías intentar tener presente utilizar la asignación compuesta del operador de suma (+=) en tu variable "calculo" ( https://www.tutorialspoint.com/compound-assignment-operators-in-chash ), lo cual no optimiza el código pero simplifica bastante la comprensión del código fuente.
De todas formas a mi no me convence el enfoque que ambos le dieron, en tu caso fíjate que al final estás tratanto con varias funciones de tratamiento de cadenas de texto como string.replace y string.trim, y luego haciendo distintas operaciones sobre un mismo valor numérico, para eso sería preferible que directamente lo simplificases construyendo un string completo con la expresión matemática y pasárselo a NCalc, pero bueno, por otro lado pienso que cualquier opción o solución es igual de válida mientras el resultado sea exitoso.

Cita de: rigorvzla en 10 Febrero 2021, 18:17 PMpero si , con Ncalc simplifico esto estaria espectacular

Poder se podría siempre y cuando le des el enfoque de construir un string / expresión matemática en lugar de hacerlo como lo estás haciendo actualmente, pero ya no merece la pena mientras tu código actual te funcione de forma exitosa no hay necesidad real de desecharlo o adaptarlo para hacer las cosas de forma distinta. Lo que yo te diría es que intentes considerar NCalc para futuras necesidades, por que te podrá venir de perlas.

Saludos!

Tachikomaia

Yo veo un error en el "-". En los otros 3 símbolos se repiten 2 veces, pero en este caso hay un +.