Excel e gli applicativi Microsoft Office Sfida di Pasqua: calcolatore con notazione polacca

Login Registrati
Stai vedendo 25 articoli - dal 1 a 25 (di 52 totali)
  • Autore
    Articoli
  • #45114 Score: 0 | Risposta

    vecchio frac
    Senior Moderator
      272 pts

      Questa sfida e' di media complessita'. Diciamo che chi ottiene un risultato apprezzabile, sa il fatto suo   

      Si tratta di costruire un semplice calcolatore (magari limitiamolo alle quattro operazioni, pero' in astratto virtualmente sarebbero possibili tutte le operazioni di una calcolatrice professionale) che agisca mediante la "notazione polacca".

      Per i dettagli sulla notazione polacca vi rimando a Wikipedia: https://it.wikipedia.org/wiki/Notazione_polacca

      In soldoni, la notazione polacca, o notazione prefissa, è una particolare sintassi matematica in cui gli operatori si trovano tutti a sinistra degli argomenti. Questo significa che possiamo scrivere formule senza l’uso di parentesi o altri elementi di separazione. 
      Ad esempio l'espressione "+  5 * 3 2" deve dare come risultato 11: in notazione infissa (quella che utilizziamo in aritmetica) sarebbe "5 + 3 * 2" dove ovviamente la moltiplicazione ha la precedenza sull'operatore di addizione. E come vedete non servono le parentesi perche' l'ordine di calcolo e' dato dalla posizione degli operatori.

      La sfida e' interessante perche' prevede (per ottimizzare il codice) l'uso della ricorsione.

      La procedura dovrebbe accettare una stringa in ingresso, valutarla e produrre il risultato. Un miglior risultato prevede anche la gestione degli errori. Potete fornire Sub o Function come volete, pero' sappiate che faro' dei test di funzionamento   

      Vi lascio alcune stringhe di espressione di prova:
      "- + 2 * / 6 2 4 1"  ~~~> 13   cioe'  (((6/2) * 4) + 2) - 1
      "* / 15 3 2"  ~~~> 10    cioe' (15/3) * 2
      "/ 15 * 3 2"  ~~~> 2,5   cioe' 15 / (3*2)
      "/ 3 0"  ~~~> errore (divisione per zero)    cioe' 3/0

      Scossa e' invitato a produrre una soluzione commentata cosi' anche noi poveri mortali ci capiamo qualcosa   

      Buon lavoro e grazie in anticipo a chi vorra' provarci!   

      #45117 Score: 0 | Risposta

      scossa
      Partecipante
        37 pts

        vecchio frac ha scritto:

        Ad esempio l'espressione "+  5 * 3 2" deve dare come risultato 11: in notazione infissa (quella che utilizziamo in aritmetica) sarebbe "5 + 3 * 2"

        Da utilizzatore di calcolatrici HP fin da metà anni '70, ho sempre amato la RPN (Notazione Polacca Inversa): "2 3 * 5 +" cioè "2 Enter 3 * 5 +" che nello stack diventa:

        input		display	
        2			2
        Enter		2,00
        3			3
        *			6,00
        5			5
        +			11,00
        

        Sfida interessante, tempo permettendo proverò a cimentarmi.

        #45123 Score: 0 | Risposta

        vecchio frac
        Senior Moderator
          272 pts

          scossa ha scritto:

          Da utilizzatore di calcolatrici HP fin da metà anni '70, ho sempre amato la RPN

          Lo sapevo, ci avrei scommesso qualcosa    in effetti sarebbe piu' usuale. Ma anche piu' facile chiaramente. Per questo penso che una cosa meno intuitiva sarebbe piu' utile anche agli altri (la mia prevede la ricorsione, forse ci sono altri modi).

          Grazie scossa   

          #45130 Score: 0 | Risposta

          Luca73
          Partecipante
            58 pts

            Ciao

            se ho capito bene ogni operatore lavora tra i due successivi numeri che trova alla sua destra. Se alla sua destra trova un altro operatore (op2) quest'ultimo prende i due numeri alla sua destra e il risultato viene usato per l'operatore originario

            "- + 2 * / 6 2 4 1"  ~~~> 13   cioe'  (((6/2) * 4) + 2) - 1

            Domanda 1

            "- + 3 * / 6 2 5 1"  ~~~> 17   cioe'  (((6/2) * 5) + 3) - 1  ??

            Domanda 2

            "- + 2 * + 6 2 4 1"     vale  (((6+2) * 4) + 2) - 1 oppure   6+2 * 4 + 2 - 1 ovvero 6+(2 * 4) + 2 - 1

            Domanda 2

            "* + 2 * + 6 2 4 1"     vale  (((6+2) * 4) + 2) - 1 oppure   6+2 * 4 + 2 - 1 ovvero 6+(2 * 4) + 2 - 1

            "* + 2 * + 6 2 4 3"     vale  (((6+2) * 4) + 2) *3 oppure   6+2 * 4 + 2 *3 ovvero 6+(2 * 4) + (2 * 3)

            Sinceramente ho qualche difficoltà a capire l'ordine delle operazioni se segue la notazione (ovvero da sinistra verso destra) oppure se segue l'ordine delle operazione (ovvero prima * e / e poi +e-) 

             

            #45133 Score: 0 | Risposta

            vecchio frac
            Senior Moderator
              272 pts

              La logica di fondo e': un operatore e due operandi (perche' per semplicita' consideriamo solo le quattro operazioni fondamentali... eventuali ampliamenti possono essere discussi piu' avanti).

              Quando incontri un operatore, poi per forza devi cucire insieme i due numeri successivi (ricordo che c'e' sempre solo uno spazio a separarli). Quindi:

              Luca73 ha scritto:

              Domanda 1

              "- + 3 * / 6 2 5 1"  ~~~> 17   cioe'  (((6/2) * 5) + 3) - 1  ??

              verrebbe computato come segue: 6/2 poi il risultato moltiplicato per 5 poi il risultato viene aggiunto a 3 poi il risultato viene sottratto a 1. Risultato 17.

              Luca73 ha scritto:

              Domanda 2

              "- + 2 * + 6 2 4 1"     vale  (((6+2) * 4) + 2) - 1 oppure   6+2 * 4 + 2 - 1 ovvero 6+(2 * 4) + 2 - 1

              Deve dare 33. Infatti calcoli 6+2 che moltiplichi per 4 cui aggiungi 2 e sottrai 1.

              Luca73 ha scritto:

              "* + 2 * + 6 2 4 1"     vale  (((6+2) * 4) + 2) - 1 oppure   6+2 * 4 + 2 - 1 ovvero 6+(2 * 4) + 2 - 1

              Questo deve dare 34 cioe' ((6+2) * 4  + 2) * 1.

              Luca73 ha scritto:

              "* + 2 * + 6 2 4 3"     vale  (((6+2) * 4) + 2) *3 oppure   6+2 * 4 + 2 *3 ovvero 6+(2 * 4) + (2 * 3)

              Questo restituisce 102 cioe' (((6+2) * 4) + 2)* 3.

              Luca73 ha scritto:

              Sinceramente ho qualche difficoltà a capire l'ordine delle operazioni se segue la notazione (ovvero da sinistra verso destra) oppure se segue l'ordine delle operazione (ovvero prima * e / e poi +e-) 

              Segue l'ordine in cui sono esposti gli operatori ed e' per questo che non servono le parentesi (guarda i miei primi esempi, il secondo e il terzo che sono simili ma non uguali),

              #45134 Score: 0 | Risposta

              Luca73
              Partecipante
                58 pts

                Grazie, Ora penso di aver Capito

                #45151 Score: 0 | Risposta

                scossa
                Partecipante
                  37 pts

                  vecchio frac ha scritto:

                  Per questo penso che una cosa meno intuitiva sarebbe piu' utile anche agli altri

                  Mah, io sarei partito con la più intuitiva per favorire la partecipazione; comunque io ho pronta una UDF, dimmi tu quando posso pubblicarla.

                  #45163 Score: 1 | Risposta

                  Raffaele53
                  Partecipante
                    23 pts

                    Domande a scossa
                    1) Esiste pure Inversa? Si capisce se -+*/ iniziano da destra?
                    2) Sul mio allegato, creo una stringa EX (3/0), come potrei far uscire il risultato in un msgbox (senza scrivere la formula in una cella?

                    Ps. Non sono molto bravo, mi sembra d'aver trovato un metodo per le stringhe che avete allegato sul forum

                    Allegati:
                    You must be logged in to view attached files.
                    #45175 Score: 0 | Risposta

                    scossa
                    Partecipante
                      37 pts

                      Sinceramente mi viene il dubbio di non aver compreso il quiz   

                      Lo scopo è quello di riprodurre la "logica" della "polish notation" e quindi elaborare con tale logica l'espressione (cit.: "Si tratta di costruire un semplice calcolatore"), oppure semplicemente di tradurre la stringa PN in una espressione infissa (vedi codice di Raffaele)?

                      Resto in attesa di delucidazioni. 

                      P.S.: @raffaeleNotazione polacca inversa

                       

                      #45187 Score: 0 | Risposta

                      vecchio frac
                      Senior Moderator
                        272 pts

                        scossa ha scritto:

                        io sarei partito con la più intuitiva per favorire la partecipazione

                        Finora le mie proposte sono sempre state molto facili. Proviamo ad alzare un po' il livello, cosi' magari le facciamo diventare un pochino piu' interessanti.

                        scossa ha scritto:

                        dimmi tu quando posso pubblicarla.

                        Anch'io ho pronta la mia, volevo attendere un paio di giorni per non influenzare nessuno, ma sei libero di scegliere il momento che ritieni adatto, come tutti. 

                        #45189 Score: 0 | Risposta

                        vecchio frac
                        Senior Moderator
                          272 pts

                          scossa ha scritto:

                          Sinceramente mi viene il dubbio di non aver compreso il quiz 

                          Sono sicuro invece che hai capito bene   

                          scossa ha scritto:

                          Lo scopo è quello di riprodurre la "logica" della "polish notation" e quindi elaborare con tale logica l'espressione (cit.: "Si tratta di costruire un semplice calcolatore"), oppure semplicemente di tradurre la stringa PN in una espressione infissa (vedi codice di Raffaele)?

                          In queste sfide e' gradita la fantasia... io non avrei pensato a una soluzione come quella di Raffaele (che trovo abbastanza semplice e funzionale perche' raggiunge l'obiettivo). Volevo costruire un parser che traducesse una stringa in un risultato numerico seguendo determinate regole. Quindi il calcolatore parte da un'espressione stringa, la rielabora nei suoi componenti costituivi, traduce i simboli che incontra e calcola un risultato (numerico o di errore). A te anzi posso anche chiedere di spingerti a costruire un calcolatore esteso, per esempio aggiungendo funzioni che accettano un solo operando e ignorano il secondo come Abs e Sqrt. Del resto quando hai costruito un algoritmo universale, ampliarlo non sara' complicato.

                          Come sapete non ci sono limiti all'ingegno, quindi bravo Raffaele che ha mostrato una soluzione funzionante   

                          #45190 Score: 0 | Risposta

                          vecchio frac
                          Senior Moderator
                            272 pts

                            Raffaele53 ha scritto:

                            2) Sul mio allegato, creo una stringa EX (3/0), come potrei far uscire il risultato in un msgbox (senza scrivere la formula in una cella?

                            Non sono scossa ma ti rispondo ugualmente   

                            Potresti utilizzare Evaluate invece che inserire T1, la stringa che contiene l'espressione, direttamente come formula di Excel. Se il risultato e' un valore di errore, lo intercetti.

                            Fine:
                            e = Evaluate(T1)
                            If IsError(e) Then MsgBox "L'espressione produce un errore."
                            Target.Offset(0, 1).Formula = "=" & T1

                            Nota: benedetto sia Option Explicit e chi l'ha inventato   

                            #45200 Score: 1 | Risposta

                            scossa
                            Partecipante
                              37 pts

                              Ciao,

                              visto che nessuno rompe il ghiaccio lo faccio io con una UDF versione base: prodotto, divisione, addizione, sottrazione e potenza (di conseguenza anche radice, visto che elevare un numero p.e 16 allo 0,5 corrisponde alla radice quadrata).

                              La funzione non è ricorsiva e simula il principio della catasta (stack) tipico della notazione Polacca mediante una Collection:

                              '---------------------------------------------------------------------------------------------------------
                              ' Procedure : fPN
                              ' Author    : scossa
                              ' Date      : 28/03/2024
                              ' Purpose   : valuta un'espressione stringa in "Notazione Polacca" (NP)
                              ' Argoments
                              '      sArg : espressione stringa da valutare
                              '
                              ' https://www.excelvba.it/forumexcel/forums/discussione/sfida-di-pasqua-calcolatore-con-notazione-polacca/
                              '---------------------------------------------------------------------------------------------------------
                              
                              Function fPN(ByVal sArg As String)
                                Const sOps As String = " * / + - ^ "
                                Dim vArg As Variant, vRet As Variant
                                Dim j As Long, nArg As Long, k As Long
                                Dim vOp As Variant, vStack As Variant
                                Dim cStack As Collection
                                
                                Set cStack = New Collection
                                
                                If sArg <> "" Then
                                  vArg = Split(Trim(sArg), " ")
                                  vStack = vArg
                                  nArg = UBound(vArg)
                                  
                                  For k = 0 To nArg
                                    vArg(nArg) = vStack(k)
                                    vStack(k) = 0
                                    nArg = nArg - 1
                                  Next k
                                  For Each vOp In vArg
                                    If InStr(sOps, " " & vOp & " ") = 0 Then
                                      cStack.Add Replace(vOp, ",", ".")
                                    Else
                                      j = cStack.Count
                                      vRet = Evaluate(CStr(cStack(j)) & vOp & CStr(cStack(j - 1)))
                                      cStack.Remove (j)
                                      cStack.Remove (j - 1)
                                      cStack.Add vRet
                                    End If
                                  Next vOp
                                Else
                                  vRet = CVErr(xlErrNA)
                                End If
                                
                                fPN = vRet
                                Set cStack = Nothing
                              End Function

                              questa la sub per testarla in VBA:

                              Sub TestPN()
                                Dim sExpr As String
                                
                                sExpr = "- + 3 * / 6 2 5 1" '17
                                Debug.Print sExpr, fPN(sExpr)
                                
                                sExpr = "- + 2 * + 6 2 4 1" '33
                                Debug.Print sExpr, fPN(sExpr)
                                
                                sExpr = "* + 2 * + 6 2 4 1"  '34
                                Debug.Print sExpr, fPN(sExpr)
                                
                                sExpr = "* + 2 * + 6 2 4 3" '102
                                Debug.Print sExpr, fPN(sExpr)
                                
                                sExpr = "/ 15 * 3 2" '2,5
                                Debug.Print sExpr, fPN(sExpr)
                                
                                sExpr = "/ 3 0" 'error
                                Debug.Print sExpr, fPN(sExpr)
                                
                                sExpr = "* / 15 3 2"  '10
                                Debug.Print sExpr, fPN(sExpr)
                                
                                sExpr = "^ 2 3"  '8
                                Debug.Print sExpr, fPN(sExpr)
                                
                                sExpr = "^ 2 0,5"  '1,41421356
                                Debug.Print sExpr, fPN(sExpr)
                              
                                sExpr = "^ 9 1/3"  '3
                                Debug.Print sExpr, fPN(sExpr)
                                  
                                sExpr = "+ 2 0,5"  '2,5
                                Debug.Print sExpr, fPN(sExpr)
                                
                              End Sub

                              Lato celle, invece, previa formattazione come testo delle celle che contengono l'espressione, p.e. in A2 - + 3 * / 6 2 5 1 nella cella a fianco:: =fPN(A2):

                              Espressione	risultato
                              - + 3 * / 6 2 5 1	17
                              - + 2 * + 6 2 4 1	33
                              * + 2 * + 6 2 4 1	34
                              * + 2 * + 6 2 4 3	102
                              / 15 * 3 2			2,5
                              / 3 0			#DIV/0!
                              * / 15 3 2			10
                              ^ 2 3				8
                              ^ 2 0,5				1,414213562
                              ^ 4 1/2				2
                              ^ 9 1/3				3
                              

                               

                              #45202 Score: 1 | Risposta

                              Aldo Ercolini
                              Partecipante
                                19 pts

                                Questa e' la mia versione

                                Allegati:
                                You must be logged in to view attached files.
                                #45204 Score: 0 | Risposta

                                vecchio frac
                                Senior Moderator
                                  272 pts

                                  scossa ha scritto:

                                  La funzione non è ricorsiva

                                  Bello. Ero curioso di vedere una soluzione con uno stack infatti. Avevo pensato anche io alla Collection per questo scopo ma poi ho lasciato perdere   

                                  #45205 Score: 0 | Risposta

                                  vecchio frac
                                  Senior Moderator
                                    272 pts

                                    scossa ha scritto:

                                    di conseguenza anche radice

                                    Non ci avevo pensato ma naturalmente e' una conseguenza logica. Nel tuo codice ogni pezzo viene prevalutato e quindi considerato come argomento per l'operazione successiva.

                                    Comunque mi ricorda il funzionamento dello stack del VIC20. Perlomeno, ero partito da quell'idea li' anche se poi non l'ho combinata granche'.

                                    #45206 Score: 0 | Risposta

                                    vecchio frac
                                    Senior Moderator
                                      272 pts

                                      Aldo Ercolini ha scritto:

                                      Questa e' la mia versione

                                      Ottimo Aldo grazie. Anche tu hai adottato una logica simile alla mia attraverso l'enumerazione delle quattro operazioni (io ho scelto un costrutto Select Case per leggibilita'). Buono anche il controllo degli errori, l'unico pugno in un occhio e' quel GoTo Err_CalcolatriceNotazionePolacca

                                      Ma va bene ugualmente    

                                      #45207 Score: 0 | Risposta

                                      Aldo Ercolini
                                      Partecipante
                                        19 pts

                                        vecchio frac ha scritto:

                                        l'unico pugno in un occhio e' quel GoTo Err_CalcolatriceNotazionePolacca

                                        Hai ragione Francesco, ma mi sono incartato e non sapevo come uscirne , ci studio meglio e vedo come risolvere.

                                        Ogni suggerimento e' bene accetto    

                                         

                                        #45208 Score: 0 | Risposta

                                        vecchio frac
                                        Senior Moderator
                                          272 pts

                                          Allego la mia proposta:

                                          Option Explicit
                                          
                                          'NOTAZIONE POLACCA
                                          'https://it.wikipedia.org/wiki/Notazione_polacca
                                          
                                          Function notazione_polacca(s As String) As Variant
                                          Dim tokens() As String
                                          Dim index As Integer
                                              
                                              s = Trim(s)
                                              tokens = Split(s)
                                              index = 0
                                              
                                              On Error GoTo ErrorHandler
                                              notazione_polacca = evaluate_token(tokens, index)
                                              
                                              If index < UBound(tokens) Then ' Controlla la coerenza dei token
                                                  Err.Raise vbObjectError + 1, "notazione_polacca", "Struttura dell'espressione non valida: troppi token."
                                              End If
                                              
                                              Exit Function
                                          
                                          ErrorHandler:
                                              notazione_polacca = CVErr(xlErrValue)
                                              MsgBox "Errore nella valutazione dell'espressione: " & Err.Description, vbCritical, "Errore di Valutazione"
                                          End Function
                                          
                                          
                                          Private Function evaluate_token(tokens() As String, ByRef index As Integer) As Double
                                          Dim token As String
                                          Dim oLeft As Double
                                          Dim oRight As Double
                                          
                                              If index > UBound(tokens) Then
                                                  Err.Raise vbObjectError + 2, "evaluate_token", "Struttura dell'espressione non valida: operandi insufficienti."
                                              End If
                                          
                                              token = tokens(index)
                                              
                                              If IsNumeric(token) Then
                                                  evaluate_token = Val(token)
                                              Else
                                                  Select Case token
                                                      Case "+", "-", "*", "/", "mod", "abs", "sqr"
                                                          index = index + 1
                                                          oLeft = evaluate_token(tokens, index)
                                                          
                                                          If token <> "abs" And token <> "sqr" Then
                                                              index = index + 1
                                                              oRight = evaluate_token(tokens, index)
                                                          End If
                                                          
                                                          Select Case token
                                                              Case "+"
                                                                  evaluate_token = oLeft + oRight
                                                              Case "-"
                                                                  evaluate_token = oLeft - oRight
                                                              Case "*"
                                                                  evaluate_token = oLeft * oRight
                                                              Case "/"
                                                                  If oRight = 0 Then Err.Raise vbObjectError + 3, "evaluate_token", "Divisione per zero."
                                                                  evaluate_token = oLeft / oRight
                                                              Case "mod"
                                                                  evaluate_token = oLeft Mod oRight
                                                              Case "abs"
                                                                  evaluate_token = Abs(oLeft)
                                                              Case "sqr"
                                                                  evaluate_token = Sqr(oLeft)
                                                          End Select
                                                      Case Else
                                                          Err.Raise vbObjectError + 4, "evaluate_token", "Token non valido:" & vbNewLine & "[" & Chr(34) & token & Chr(34) & "]"
                                                  End Select
                                              End If
                                          End Function

                                          E la sub di test:

                                          Sub test_notazione_polacca()
                                          Dim s As String
                                          Dim result As Variant
                                          
                                          '    s = "/ 15 * 3 2"               ' 2,5
                                          '    s = "* / 15 3 2"               ' 10
                                          '    s = "- + 2 * / 6 2 4 1"        ' 13
                                          '    s = "/ 3 0"                    ' error
                                          '    s = "+ 5 * 3 2"                ' 11
                                          '    s = "* + 2 * + 6 2 4 3"        ' 102
                                          '    s = " sqr + abs - 5 12 2 "     ' 3
                                              
                                              result = notazione_polacca(s)
                                              
                                              If Not isError(result) Then
                                                  MsgBox "Il risultato di " & vbNewLine & s & vbNewLine & "e' : " & result
                                              End If
                                                  
                                          End Sub
                                          #45209 Score: 0 | Risposta

                                          Aldo Ercolini
                                          Partecipante
                                            19 pts

                                            Risolto

                                            Allegati:
                                            You must be logged in to view attached files.
                                            #45211 Score: 0 | Risposta

                                            vecchio frac
                                            Senior Moderator
                                              272 pts

                                              Grazie Aldo, ma vorrei farti notare un problema di fronte ad espressioni come "1 2" oppure "+ 1 2 3". E' volutamente una condizione di errore. Il programma si inchioda in un loop che si sblocca con Ctrl-break: quindi la routine non manda Excel in crash, ma entra in un circolo vizioso... problema presente anche nella prima versione.

                                              #45212 Score: 0 | Risposta

                                              Luca73
                                              Partecipante
                                                58 pts

                                                Ciao a tutti e buona Pasqua,

                                                io in questo periodo non ce la faccio,....se mi aspettate provero a ragionarci dopo l'8 di aprile

                                                #45213 Score: 0 | Risposta

                                                scossa
                                                Partecipante
                                                  37 pts

                                                  vecchio frac ha scritto:

                                                  Err.Raise vbObjectError + 4, "evaluate_token", "Token non valido:"

                                                  sai che uso abitualmente Err.Raise, ma sai anche che, secondo me, le udf devono comportarsi come le funzioni native, cioè limitarsi a restiutire un codice di errore, niente testi esplicativi o, peggio, msgbox. Nella mia udf quindi sta all'operatore scrivere nella corretta sintassi della NP la stringa da elaborare.

                                                  #45214 Score: 0 | Risposta

                                                  Aldo Ercolini
                                                  Partecipante
                                                    19 pts

                                                    Risolto anche questo    

                                                    Allegati:
                                                    You must be logged in to view attached files.
                                                    #45216 Score: 0 | Risposta

                                                    vecchio frac
                                                    Senior Moderator
                                                      272 pts

                                                      scossa ha scritto:

                                                      sai che uso abitualmente Err.Raise

                                                      Volevo provare a fare come te. Tentativo fallito   

                                                    Login Registrati
                                                    Stai vedendo 25 articoli - dal 1 a 25 (di 52 totali)
                                                    Rispondi a: Sfida di Pasqua: calcolatore con notazione polacca
                                                    Gli allegati sono permessi solo ad utenti REGISTRATI
                                                    Le tue informazioni: