miércoles, febrero 09, 2005

 

Un reconocedor de expresiones a la brava

Uno de mis intereses desde que aprendi a programar ha sido el de generar gráficas de funciones matemáticas. Esa parte es sencilla, si la función a graficar se coloca en el Código fuente. Por supuesto, una mejor implementación permitirá al usuario introducir la ecuación que desee. La primera solución que se viene a la mente – si no se tiene conocimiento sobre compiladores - es crear una estructura fija, que el usuario pueda llenar, evitando al máximo la cantidad de errores de input. Esto limita la capacidad del programa, y requiere un cambio de la interfase se desea ampliar la funcionalidad del programa.
La mejor alternativa: que el usuario pueda introducir la expresión tal y como se escribe en notación matemática. Aquí es donde viene la parte interesante.
Como construir un reconocedor reexpresiones matemáticas sin conocimiento de herramientas para crear compiladores, y aún sin saber aplicar la teoría de autómatas?Aquí presento un primer intento. Un programa en Visual Basic.

La idea básica para reconocer era: Dado que la mayoría de las expresiones matemáticas son binarias, encontrar un operador, subdividir el input (cadena de entrada) en tres partes, izquierda, operador, derecha.
El operador se convierte en la raíz de un árbol, y se llama recursivamente este procedimiento para poblar los hijos de este arbol.
Las dificultades relativas a la precedencia se resolvieron haciendo un exceso d pasadas, ya que en una primera pasada se buscan los operadores con mayor precedencia, como la exponenciación, en una segunda los ‘por’ y ‘entre’, y en otra más, los operadores de suma y resta, y considerando la recursión, la cadena completa se recorre hasta tres veces por cada operador.
La precedencia con los paréntesis se resolvía “bloqueando” el encuentro de operadores si se estaba en un nivel de paréntesis distinto de cero, y enviando luego la cadena entre paréntesis (removiendo los mismos) al procedimiento antes descrito.
He aquí el código para examinarlo y si lo desea, compilarlo. Fue creado usando VB 6.0.
Por favor disculpar el “abuso” con el nombre, en ese entonces no sabía exactamente de que se trataba...
Instrucciones: Copy & Paste en cualquier editor de texto plano, salvar como Pharser01.frm, crear un nuevo proyecto de VB, e incluir este formulario en el.
“Hay que caerse antes de aprender a caminar”
---------------------- Archivo: pharser01.frm -------------------
VERSION 5.00
Object = "{831FDD16-0C5C-11D2-A9FC-0000F8754DA1}#2.0#0"; "MSCOMCTL.OCX"
Begin VB.Form Pharser01
BorderStyle = 1 'Fixed Single
Caption = "Pharser01"
ClientHeight = 7335
ClientLeft = 45
ClientTop = 435
ClientWidth = 10470
BeginProperty Font
Name = "MS Sans Serif"
Size = 12
Charset = 0
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
LinkTopic = "Form1"
MaxButton = 0 'False
MinButton = 0 'False
ScaleHeight = 7335
ScaleWidth = 10470
StartUpPosition = 3 'Windows Default
Begin VB.ListBox varList
Height = 5760
Left = 7560
Sorted = -1 'True
TabIndex = 6
Top = 840
Width = 2655
End
Begin VB.CommandButton btnSolve
Caption = "&Solve"
Height = 375
Left = 8880
TabIndex = 5
Top = 0
Width = 1335
End
Begin MSComctlLib.TreeView Tree
Height = 5895
Left = 0
TabIndex = 2
Top = 840
Width = 7530
_ExtentX = 13282
_ExtentY = 10398
_Version = 393217
HideSelection = 0 'False
Indentation = 706
LabelEdit = 1
LineStyle = 1
Style = 4
Appearance = 1
End
Begin VB.CommandButton btnProccess
Caption = "&Proccess"
Height = 375
Left = 7560
TabIndex = 1
Top = 0
Width = 1335
End
Begin VB.TextBox txtPharse
Height = 420
Left = 0
TabIndex = 0
Top = 0
Width = 7575
End
Begin VB.Label Label3
Caption = "Variables:"
Height = 255
Left = 7560
TabIndex = 7
Top = 600
Width = 2655
End
Begin VB.Label Label2
BorderStyle = 1 'Fixed Single
Height = 615
Left = 0
TabIndex = 4
Top = 6720
Width = 10455
End
Begin VB.Label Label1
Caption = "Arbol de expresión:"
Height = 255
Left = 0
TabIndex = 3
Top = 600
Width = 4095
End
End
Attribute VB_Name = "Pharser01"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit
Private Function MenosUnario(Expre As String, i As Integer) As Boolean
If i = 1 Then MenosUnario = True: Exit Function
If esOper(Mid(Expre, i - 1, 1)) Then MenosUnario = True: Exit Function
'if i > 2 and esOper(Mid(Expre, i - 2, 2)) Then MenosUnario = True: Exit Function
End Function

Sub Arbol(Expre As String, Optional index As Integer)
If Expre = "" Then Exit Sub
Dim aux As Node
Dim i As Integer, par As Integer: par = 0
Dim ParMid As Boolean
'si la cadena empieza y termina en parentesis, y no hay parentesis intermedios
'removemos los extremos.
For i = 2 To Len(Expre) - 1
If Mid(Expre, i, 1) = "(" Or Mid(Expre, i, 1) = ")" Then ParMid = True: Exit For
Next
If Not ParMid Then
If Mid(Expre, 1, 1) = "(" And Mid(Expre, Len(Expre), 1) = ")" Then Expre = Mid(Expre, 2, Len(Expre) - 2)
End If
If Len(Expre) = 0 Then Exit Sub
'Operadores Logicos( &: AND, : OR, !:NOT, ?:XOR
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If Mid(Expre, i, 1) = "&" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "&")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "&")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = "" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
End If
If Mid(Expre, i, 1) = "!" And par = 0 Then
If i = 1 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "!")
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "!")
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
End If
End If
If Mid(Expre, i, 1) = "?" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "?")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "?")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
End If
Next
'Signos de Comparacion
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If i > 1 Then
If Mid(Expre, i - 1, 2) = ">=" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , ">=")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , ">=")
End If
Arbol Mid(Expre, 1, i - 2), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i - 1, 2) = "<=" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "<=")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "<=")
End If
Arbol Mid(Expre, 1, i - 2), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
End If
If Mid(Expre, i, 1) = "=" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "=")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "=")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = ">" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , ">")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , ">")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = "<" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "<")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "<")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If

Next
'signos de suma ( +, -)
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If Mid(Expre, i, 1) = "+" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "+")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "+")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = "-" And par = 0 Then
If Not MenosUnario(Expre, i) Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "-")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "-")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
Else 'menos unario
If i = 1 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "_")
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "_")
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
End If
End If
End If
Next
'Signos de multiplicacion ( /, *)
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If Mid(Expre, i, 1) = "*" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "*")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "*")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = "/" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "/")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "/")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
Next
'Signo de exponenciacion
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If Mid(Expre, i, 1) = "^" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "^")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "^")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
Next
'si no habia signos en la cadena:
If index = 0 Then
Tree.Nodes.Add , , , Expre
Else
Tree.Nodes.Add index, tvwChild, , Expre
End If
End Sub
Sub Arbol2(Expre As String, Tree As TreeView, Optional index As Integer)
If Expre = "" Then Exit Sub
Dim aux As Node
Dim i As Integer, par As Integer: par = 0
Dim ParMid As Boolean
'si la cadena empieza y termina en parentesis, y no hay parentesis intermedios
'removemos los extremos.
For i = 2 To Len(Expre) - 1
If Mid(Expre, i, 1) = "(" Or Mid(Expre, i, 1) = ")" Then ParMid = True: Exit For
Next
If Not ParMid Then
If Mid(Expre, 1, 1) = "(" And Mid(Expre, Len(Expre), 1) = ")" Then Expre = Mid(Expre, 2, Len(Expre) - 2)
End If
If Len(Expre) = 0 Then Exit Sub
'Operadores Logicos( &: AND, : OR, !:NOT, ?:XOR
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If Mid(Expre, i, 1) = "&" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "&")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "&")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = "" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
End If
If Mid(Expre, i, 1) = "!" And par = 0 Then
If i = 1 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "!")
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "!")
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
End If
End If
If Mid(Expre, i, 1) = "?" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "?")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "?")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
End If
Next
'Signos de Comparacion
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If i > 1 Then
If Mid(Expre, i - 1, 2) = ">=" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , ">=")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , ">=")
End If
Arbol Mid(Expre, 1, i - 2), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i - 1, 2) = "<=" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "<=")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "<=")
End If
Arbol Mid(Expre, 1, i - 2), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
End If
If Mid(Expre, i, 1) = "=" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "=")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "=")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = ">" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , ">")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , ">")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = "<" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "<")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "<")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If

Next
'signos de suma ( +, -)
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If Mid(Expre, i, 1) = "+" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "+")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "+")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = "-" And par = 0 Then
If Not MenosUnario(Expre, i) Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "-")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "-")
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
Else 'menos unario
If i = 1 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "_")
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "_")
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
End If
Exit Sub
End If
End If
End If
Next
'Signos de multiplicacion ( /, *)
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If Mid(Expre, i, 1) = "*" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "*")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "*")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
If Mid(Expre, i, 1) = "/" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "/")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "/")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
Next
'Signo de exponenciacion
For i = Len(Expre) To 1 Step -1
If Mid(Expre, i, 1) = "(" Then par = par + 1
If Mid(Expre, i, 1) = ")" Then par = par - 1
If par > 0 Then Label2.Caption = "error de parentesis: " & Expre: Exit Sub
If Mid(Expre, i, 1) = "^" And par = 0 Then
If index = 0 Then
Set aux = Tree.Nodes.Add(, , , "^")
Else
Set aux = Tree.Nodes.Add(index, tvwChild, , "^")
End If
Arbol Mid(Expre, 1, i - 1), aux.index
Arbol Mid(Expre, i + 1, Len(Expre) - i), aux.index
Exit Sub
End If
Next
'si no habia signos en la cadena:
If index = 0 Then
Tree.Nodes.Add , , , Expre
Else
Tree.Nodes.Add index, tvwChild, , Expre
End If
End Sub

Private Function esOper(str As String) As Boolean
Select Case str
Case "+": esOper = True: Exit Function
Case "-": esOper = True: Exit Function
Case "_": esOper = True: Exit Function
Case "*": esOper = True: Exit Function
Case "/": esOper = True: Exit Function
Case "^": esOper = True: Exit Function

Case "=": esOper = True: Exit Function
Case ">": esOper = True: Exit Function
Case "<": esOper = True: Exit Function
Case ">=": esOper = True: Exit Function
Case "<=": esOper = True: Exit Function

Case "&": esOper = True: Exit Function
Case "": esOper = True: Exit Function
Case "!": esOper = True: Exit Function
Case "?": esOper = True: Exit Function
End Select
esOper = False
End Function

Function palabra(signo As String) As String
Select Case palabra
Case "+": signo = "MAS"
Case "-": signo = "MENOS"
Case "_": signo = "NEG"
Case "*": signo = "POR"
Case "/": signo = "ENTRE"
Case "^": signo = "ELEVADO A"
Case "=": signo = "IGUAL A"
Case ">": signo = "MAYOR QUE"
Case "<": signo = "MENOR QUE"
Case ">=": signo = "MAYOR O IGUAL QUE"
Case "<=": signo = "MENOR O IGUAL QUE"
Case "&": esOper = "AND"
Case "": esOper = "OR"
Case "!": esOper = "NOT"
Case "?": esOper = "XOR"
End Select
End Function

Private Function Simetrical(Tree As TreeView) As String
Dim temp As String
If Tree.Nodes.Count = 0 Then
Simetrical = "NULL"
Exit Function
Else
Simetrical = SimOrder(Tree.Nodes(1))
End If
End Function
Function SimOrder(NODO As Node) As String
If NODO.Children = 0 Then
SimOrder = " " & NODO.Text & " "
Else
If NODO.Children = 2 Then
SimOrder = "(" & SimOrder(NODO.Child) & NODO.Text & SimOrder(NODO.Child.Next) & ")"
Else
SimOrder = "(" & NODO.Text & SimOrder(NODO.Child) & ")"
End If
End If
End Function
Function Solve(NODO As Node) As Double
If NODO.Children = 0 Then
If IsNumeric(NODO.Text) Then
Solve = CDbl(NODO.Text)
Else
Solve = Variable(NODO.Text)
End If
Else
Select Case NODO.Text
Case "+": Solve = Solve(NODO.Child) + Solve(NODO.Child.Next)
Case "-": Solve = Solve(NODO.Child) - Solve(NODO.Child.Next)
Case "_": Solve = -Solve(NODO.Child)
Case "*": Solve = Solve(NODO.Child) * Solve(NODO.Child.Next)
Case "/": Solve = Solve(NODO.Child) / Solve(NODO.Child.Next)
Case "^": Solve = Solve(NODO.Child) ^ Solve(NODO.Child.Next)
Case "=":
If Solve(NODO.Child) = Solve(NODO.Child.Next) Then
Solve = 1
Else
Solve = 0
End If
Case ">":
If Solve(NODO.Child) > Solve(NODO.Child.Next) Then
Solve = 1
Else
Solve = 0
End If
Case "<":
If Solve(NODO.Child) < Solve(NODO.Child.Next) Then
Solve = 1
Else
Solve = 0
End If
Case ">=":
If Solve(NODO.Child) >= Solve(NODO.Child.Next) Then
Solve = 1
Else
Solve = 0
End If
Case "<=":
If Solve(NODO.Child) <= Solve(NODO.Child.Next) Then
Solve = 1
Else
Solve = 0
End If
Case "&":
If Solve(NODO.Child) And Solve(NODO.Child.Next) Then
Solve = 1
Else
Solve = 0
End If
Case "":
If Solve(NODO.Child) Or Solve(NODO.Child.Next) Then
Solve = 1
Else
Solve = 0
End If
Case "!":
If Solve(NODO.Child) = 0 Then
Solve = 1
Else
Solve = 0
End If
Case "?": 'XOR
If Solve(NODO.Child) Xor Solve(NODO.Child.Next) Then
Solve = 1
Else
Solve = 0
End If
End Select
End If
End Function

Private Function VarDefn(var As String) As Boolean
'Averigua si una cadena corresponde con una variable ya definida
Dim i As Integer
For i = 0 To varList.ListCount - 1
If InStr(1, varList.List(i), var) <> 0 Then VarDefn = True: Exit Function
Next
VarDefn = False
End Function

Private Function Variable(var As String) As Double
'Busca en la lista de variables la definición y devuelve el valor asociado
Dim i As Integer
For i = 0 To varList.ListCount - 1
If InStr(1, varList.List(i), var) <> 0 Then
Variable = CDbl(Right(varList.List(i), Len(varList.List(i)) - InStr(1, varList.List(i), "=", vbTextCompare)))
Exit Function
End If
Next
End Function

Private Sub btnProccess_Click()
'Preproceso de la expresión
'Construir el arbol de expresión
If txtPharse.Text <> "" Then Tree.Nodes.Clear: Arbol2 txtPharse.Text, Tree
'Revisa el arbol:
Dim i As Integer
For i = 1 To Tree.Nodes.Count
Tree.Nodes(i).Expanded = True 'Expande todos los nodos para hacerlos visibles
Tree.Nodes(i).Text = Trim(Tree.Nodes(i).Text) 'Elimina los espacios antes y despues de la cadena
'Si un nodo contiene texto no numérico, no es operador ni variable definida, entonces:
If Not IsNumeric(Tree.Nodes(i).Text) And Not esOper(Tree.Nodes(i).Text) And Not VarDefn(Tree.Nodes(i).Text) Then
'Define la variable con el valor por defecto 0.0
varList.AddItem Tree.Nodes(i).Text & " = 0.0"
End If
Next
End Sub

Private Sub btnSolve_Click()
'Muestra la expresión que fue reconocida y la solución de la misma
Label2.Caption = Simetrical(Tree) & " = " & CStr(Solve(Tree.Nodes(1)))
End Sub

Private Sub txtPharse_KeyDown(KeyCode As Integer, Shift As Integer)
'Reacciona a la tecla <>
If KeyCode = vbKeyReturn Then btnProccess_Click
End Sub

Private Sub varList_DblClick()
'Edicion de variable
Dim str As String, igual As Integer
str = varList.List(varList.ListIndex)
igual = InStr(1, str, "=", vbTextCompare)
str = Mid(str, 1, igual) & " " & InputBox("Definir variable" & vbNewLine & Mid(str, 1, igual), , Mid(str, igual + 1, Len(str) - igual))
If IsNumeric(Mid(str, igual + 1, Len(str) - igual)) Then varList.List(varList.ListIndex) = str
End Sub

---------------- fin de archivo ---------------------------

Comments:
Un proyecto que yo tuve que hacer cuando estudiaba en Progra II-Estructuras de Datos (ahh no...no lo hice yo sino la otra sección, pero me enteré...), fue de hacer un analizador de expresiones aritméticas, pero por medio de dos stacks: uno de numeros y otro de operadores. Al encontrar un numero, se hacia push()y al encontrar un operador, se hacia pop() de los operandos. Bueno, asi operan las calculadoras HP con RPN, pero no es necesario "escribir" el input en RPN, podia leerlo en notación aritmética normal.
 
Publicar un comentario

<< Home

This page is powered by Blogger. Isn't yours?