Trovare yap Manipolazione di stringhe



  • Trovare "yap". Manipolazione di stringhe
    di Vecchio Frac data: 06/09/2013 09:51:12

    Piccolo svago, per chi vuol giocare un po con le stringhe :)
    Ho trovato questa discussione in un forum su un altro linguaggio e vi ripropongo il quesito.
    Si tratta di trovare il numero di "yap" presenti in una stringa ricevuta in input.
    Uno "yap" è un gruppo di tre lettere che inizia proprio per 'y' e finisce proprio per 'p'.
    Per esempio "yap", "yup", "yuppi". Attenti che "yypp" è una stringa con due yap :)

    Io ho già buttato giù almeno tre soluzioni (tre Functions che restituiscono un numero).
    E ho anche messo insieme una procedura di test che richiama in sequenza le Functions di prova senza doverle richiamare singolarmente... ve la mostrerò perchè sarà interessante vedere come ho richiamato una Function indicizzata con passaggio di parametri.

    Chi vuole provare? ricchi premi e cotillon ^_^





  • di totino71 (utente non iscritto) data: 06/09/2013 09:57:07

    Uno "yap" è un gruppo di tre lettere che inizia proprio per 'y' e finisce proprio per 'p'.
    Per esempio "yap", "yup", "yuppi"
    Ma yuppi finisce per "i"



  • di Vecchio Frac data: 06/09/2013 10:19:24

    Grazie per la precisazione ciccio ^_^
    Per essere proprio espliciti ripeto che bisogna considerare i gruppi di TRE lettere (nella stringa "yuppi" ci sono tre gruppi di tre lettere e uno di questi soddisfa la condizione iniziale "y...p").

    Meno male che torturygno c'è ^_^





  • di HarryBosch data: 06/09/2013 10:32:29

    Mi piacciono queste idee ^_^
    Intanto butto giù la prima cosa che mi è venuta in mente...
     
    Sub conta_yap()
        Dim s As String, tot As Byte
        s = "fasfydpdafdaydpdafdayddpydpydpyp"
        tot = yap(s)
    End Sub
    
    Function yap(s As String) As Byte
        Dim i As Byte
        For i = 1 To Len(s) - 2
            If Mid(s, i, 3) Like "y?p" Then yap = yap + 1
        Next
    End Function



  • di tostygno (utente non iscritto) data: 06/09/2013 10:44:56

    Ma le tre lettere devono essere consecutive allora...
    altrimenti yuppi ne avrebbe 2

    YUPpi e YUpPi



  • di HarryBosch data: 06/09/2013 11:02:54

    Giusto Totygno: le maiuscole!
    Piccola revisione della Function...
     
    Function yap(s As String) As Byte
        Dim i As Byte
        For i = 1 To Len(s) - 2
            If UCase(Mid(s, i, 3)) Like "Y?P" Then yap = yap + 1
        Next
    End Function



  • di Vecchio Frac data: 06/09/2013 11:30:49

    @totygno
    Sì: devi pensare unicamente a un gruppo di tre lettere isolato, partendo dalla prima lettera. Il gruppo quindi è formato da "y" all'inizio, "p" alla fine, e qualsiasi altor carattere i mezzo.
    Come ha scritto Harry nella sua Like, "y?p".

    @Harry
    Questa soluzione è identica a una delle mie ^_^
    anche se il mio valore di ritorno non è Byte ma Long... si sa mai che abbia più di 255 occorrenze della sottostringa ^_^





  • di tontyno71 (utente non iscritto) data: 06/09/2013 11:38:09

    @Harry

    Grazie come suol dirsi: "Un fesso ti apre la mente!!!" ^_^



  • di TOstringhio (utente non iscritto) data: 06/09/2013 11:44:56

    Anche io avevo pensato la stessa cosa :P
    Ma quando mai...
    Io con vba sono una asino ma con le stringhe sono proprio un caprone...
    Mi associo alla soluzione di harry ^_^



  • di Vecchio Frac data: 06/09/2013 11:54:47

    Dai pigrone non ti associare ^_^
    Pensa a qualcosa di diverso (magari con le regex :P )





  • di tontyno (utente non iscritto) data: 06/09/2013 12:25:00

    Regex??? Non si dico parolaccie nel forum dovresti saperlo! ^_^



  • di HarryBosch data: 07/09/2013 01:26:22

    Devo ammettere che le stringhe non mi sono mai concentrato molto... preferisco di gran lunga i numeri ^_^
    Lo sapevo che non dovevo mettermi a quest'ora! Sulle espressioni regolari ci sono interi poemi sulla ricerca delle sottostringhe, in particolare nella ricerca del "pattern". Già mi è venuto mal di testa :)

    Comunque, ecco una soluzione sulla quale il più esperto VecchioFrac vorrà forse suggerirne miglioramenti o qualche trucchetto. In particolare, vorrà suggerire come conteggiare la stringa quando si ripete all'interno della stessa:
    cit:--> "yypp" sono due: yyp e ypp
    Nel mio esempio vengono contate tutte le sottostringhe "y?p" consecutive. Nella variabile REMatches (una vera e propria collection) saranno collezionate tutte lo sottostringhe nei vari Item.

    E' necessario l'aggiunta della libreria:
    Microsoft VBScript Regular Expressions 5.5
    in questo caso si può sfruttare l'intellicence con l'evidente vantaggio di trovare i metodi a disposizione di TAB...

    Altrimenti, si può farne a meno dichiarando le variabili come Object e creando l'oggetto regexp:
    Dim RE As Object, REMatches As Object
    Set RE = CreateObject("vbscript.regexp")

    Nell'attesa magari di qualche nuovo intervento...
     
    Sub conta_yap()
      Dim s As String, tot As Long
      s = "FfgDgSAYapAFDffDSyEPFdfagfSDFDFAfgsdYiPDAFDYUPP"
      tot = REG(s)
    End Sub
    
    'necessita di libreria 'Microsoft VBScript Regular Expressions 5.5:
    Function REG(s As String) As Long
      Dim REMatches As MatchCollection
      Dim RE As New RegExp
    
      With RE
        .MultiLine = True
        .Global = True
        .IgnoreCase = False
        .Pattern = "Y.P"
      End With
    
      Set REMatches = RE.Execute(UCase(s))
      REG = REMatches.Count
    
    End Function
    



  • di HarryBosch data: 07/09/2013 01:29:36

    C'è anche un UCase che poteva essere evitato dichiarando True la relativa proprietà:
    .IgnoreCase = True



  • di Vecchio Frac data: 08/09/2013 14:22:24

    Bravi i miei sperimentatori ^_^
    La soluzione con le regex di Harry però fallisce inserendo "yypp" dove gli yap sono due :)
    Domani dall'ufficio posterò la mia soluzione completa (quattro versioni diverse e un generatore di test altrettanto interessante).





  • di Vecchio Frac data: 09/09/2013 08:57:02

    Vi allego le mie quattro proposte, di complessità crescente.
    Si tratta di quattro Functions distinte con passaggio della stringa da analizzare (restituiscono il numero di corrispondenze trovate).
    Per testare le Functions ho realizzato la Sub finale, test_find_yaps, che lancia le quattro Functions in sequenza, passando per quattro volte ciascuna una stringa diversa, in modo da verificarne il comportamento in casi diversi. Il risultato è esposto nella finestra Immediata.
    E' interessante l'uso di Run(macro, parametro) con cui richiamo le quattro function pubbliche con passaggio di parametri. Mi basta un solo Run perchè ho costruito il nome delle functions con un indice numerico finale, che passo a Run ad ogni ciclo For; Run accetta una stringa come nome della function da eseguire e io gliela fornisco costruendola al volo, così è contento e fa il suo lavoro :)

     
    Option Explicit
    
    Function find_yaps1(s As String) As Long
    'soluzione 1: utilizzo di Mid, Left e Right
    Dim i As Long, z As String, cnt As Long
    
        s = LCase(s)
        For i = 1 To Len(s) - 2
            z = Mid(s, i, 3)
            If Left(z, 1) = "y" And Right(z, 1) = "p" Then cnt = cnt + 1
        Next
        
        find_yaps1 = cnt
    End Function
    
    
    Function find_yaps2(s As String) As Long
    'soluzione 1: utilizzo di Like
    Dim i As Long, z As String, cnt As Long
    
        s = LCase(s)
        For i = 1 To Len(s) - 2
            z = Mid(s, i, 3)
            If z Like "y?p" Then cnt = cnt + 1
        Next
        
        find_yaps2 = cnt
    End Function
    
    
    Function find_yaps3(s As String) As Long
    'soluzione 3: utilizzo delle regex (espressioni regolari)
    Dim re As Object
        s = LCase(s)
        
        Set re = CreateObject("VBScript.RegExp")
        re.Pattern = "(?=(y.p))"
        
        re.IgnoreCase = True        'ignore case
        re.Global = True            'matches all occurances
        
        find_yaps3 = re.Execute(s).Count
    End Function
    
    
    Function find_yaps4(s As String)
    'soluzione 4: array di byte
    Dim i As Long, v() As String, b() As Byte, cnt As Integer, t As Long
        
        b() = LCase(s)
        For i = 0 To UBound(b) - 4 Step LenB("A")
            If b(i) = 121 And b(i + 4) = 112 Then cnt = cnt + 1  ' 121 = "y", 112 = "p"
        Next
    
        find_yaps4 = cnt
    End Function
    
    
    Sub test_find_yaps()
    Dim s As String, m As String, i As Integer, j As Integer
    Const NUM_TESTs = 4
    
        Debug.Print "Start ---------------------------------"
        For i = 1 To NUM_TESTs
            s = CStr(i)
            For j = 1 To 4
                Select Case j
                Case 1
                    m = "yap"   'correct: 1
                Case 2
                    m = "yypp"  'correct: 2
                Case 3
                    m = "yaap"  'correct: 0
                Case 4
                    m = "ayapo" 'correct: 1
                End Select
                Debug.Print "Running find_yaps" & s & ": yaps in '" & m & "'... " & Run("find_yaps" & s, m)
            Next j
            Debug.Print "- - - - - - - - - - - - - - - - - - - -"
        Next i
        Debug.Print "End -----------------------------------"
    End Sub






  • di Grograman data: 09/09/2013 10:00:11

    Vedo solo ora questa discussione e ho capito che devo imparare moduli di classe e regular expression, ma mi sa che mi serve un buon testo di appoggio ^_^



  • di Vecchio Frac data: 09/09/2013 11:14:28

    cit. "ho capito che devo imparare moduli di classe"
    ---> io non ho utilizzato moduli di classe nei miei esempi ^_^
    Per le regex consiglio di partire dal sito dell'amico "r":
    sites.google.com/site/e90e50/vbscript/regexp





  • di Grograman (utente non iscritto) data: 09/09/2013 11:27:26

    Sìsì i moduli di classe è una cosa che lo so non c'entra nulla con il post, ma è un pò che mi sono detto di doverli imparare, e visto che qui si parla di regular expression ho raggruppato le idee ;)



  • di Vecchio Frac data: 09/09/2013 11:36:09

    Vuoi la verità?
    Non ho *mai* usato moduli di classe nei miei progetti VBA.
    Il mondo Office espone tutti gli oggetti che ci servono senza un reale bisogno di doverne inventare di nuovi.
    Quando mi è capitato di farlo, ho realizzato dei progettini solo a scopo didattico, tanto per imparare oggetti e classi personalizzati (tutta un'altra storia rispetto a un vero linguaggio orientato agli oggetti).
    L'unica applicazione pratica interessante riguarda la famigerata matrice di controlli ^_^