Parametri di una Function



  • Parametri di una Function
    di vmontal data: 27/03/2014 10:34:25

    Save a tutti,
    ho la necessità di scrivere una Function a cui devo passare oltre a dei valori anche l'Operatore di confronto (scusate l'italiano sporco) che devo svolgere;esempio

    Calcola(Valore1 as Integer, Valore2 as Integer, Operatore as String) as Integer
    IF Valore1 & operatore & Valore2 Then
    calcola= Valore1
    Else
    Calcola= Valore2
    Endif
    End function

    In cui Operatore può Essere ">";"<";"=";">=";"<="
    Scritta così ovviamente non funziona e mi restituisce l'errore run-time "424" - Necessario Oggetto;

    Qualcuno riesce a darmi dei suggerimenti?



  • di Vecchio Frac data: 27/03/2014 11:17:45

    1) Puoi passare alla Function l'operatore che desideri sotto forma di stringa e poi valutare mediante Select Case ogni caso possibile.
    Esempio:
    private function Calcola(Valore1 as Integer, Valore2 as Integer, Operatore as String) as Integer
    select case operatore
    case "<", "=", "<="
    if valore1 < valore2 then calcola = valore1
    case ">", ">="
    if valore2 > valore1 then calcola = valore2
    end select
    End function


    2) Puoi usare Iif:
    Private Function Calcola(Valore1 As Integer, Valore2 As Integer, Operatore As String) As Integer
    Calcola = IIf(Valore1 <= Valore2, Valore1, Valore2)
    End Function


    ma anche senza inserirlo in una Function va benisismo:
    calcola = IIf(valore1 <= valore2, valore1, valore2)



    3) Se non servono i valori di ritorno puoi usare Evaluate per stabilire se la condizione è soddisfatta in funzione dell'operatore passato (restituisce -1 per True, 0 per False):
    private function Calcola(Valore1 as Integer, Valore2 as Integer, Operatore as String) as Integer
    calcola = Evaluate(valore1 & " " & operatore & " " & valore2)

    End function





  • di Vecchio Frac data: 27/03/2014 11:19:53

    Edit.
    Nel caso 2 inserito in una Function, passare l'Operatore è ininfluente visto che non lo utilizzo.

    Private Function Calcola(Valore1 As Integer, Valore2 As Integer) As Integer
    Calcola = IIf(Valore1 <= Valore2, Valore1, Valore2)
    End Function





  • di vmontal data: 27/03/2014 11:38:46

    Ottimi Spunti di lavoro; Grazie davvero!
    La soluzione con Evaluate mi sembrava la più pulita, ma mi accorgo che se uso delle date come valori il confronto non funziona; la mia Function deve essere la più astratta possibile per cui non so a priori su che tipo di dati devo effettuare il confronto (Stringhe, Numeri, Date). (In effetti avrei dovuto scrivere Valore1 as Variant , Valore2 as Variant, Operatore as String)

    Ma comunque adattando il primo esempio penso che userò la soluzione con il Select Case!
    Grazie ancora.



  • di scossa data: 27/03/2014 21:49:48

    Non mi è chiara la tua necessità reale.

    a) I valori sono solo integer?
    b) ti serve una function da usare nel VBA o anche in una cella?
    c) I valori sono nelle celle o sono variabili VBA?



  • di vmontal data: 27/03/2014 22:15:20

    Rispondo prima con ordine:
    1) i valori possono essere di qualunque tipi (Integer, String, date)
    2) la Function mi serve in VBA;
    3) i valori sono nelle celle;

    Di seguito il codice che riproduce la funzione ContaSe; ed aggiungo per anticipare la domanda che ho dovuto ricorrere ad una Function personale piuttosto che della WorkSheetFunction.countif() perché (che ci crediate o no) in Excel 2003 utilizzare le WorkSheetFunction è un terno al lotto!
     
    Function ContaSe(Elenco As Range, Operatore As String, Valore As Variant) As Integer
        Dim I As Integer
        
        ContaSe = 0
        For I = 1 To Elenco.Count
        
            Select Case Operatore
                Case ">"
                    If Elenco(I) > Valore Then
                        ContaSe = ContaSe + 1
                    End If
                Case "<"
                    If Elenco(I) < Valore Then
                        ContaSe = ContaSe + 1
                    End If
                Case "="
                    If Elenco(I) = Valore Then
                        ContaSe = ContaSe + 1
                    End If
                Case ">="
                    If Elenco(I) >= Valore Then
                        ContaSe = ContaSe + 1
                    End If
                Case "<="
                    If Elenco(I) <= Valore Then
                        ContaSe = ContaSe + 1
                    End If
                
                Case Else
                    MsgBox "Operatore errato! Valori ammessi <,<=,=,>=,>"
                            
            End Select
            
        Next
    
    End Function
    



  • di Zer0Kelvin data: 27/03/2014 22:36:35

    Ciao.
    Secondo me, puoi semplicemente usare Evaluate; e non mi sembra che non funzioni con le date, dal momento che esse non sono altro che valori numerici (devono essere vere date, non stringhe) ed utilizzare una function che restituisca un valore logico in base a valori e operatore fornito
     
    Function confronta(V1 As Variant, V2 As Variant, Op As String) As Boolean
       confronta = Evaluate(V1 & Op & V2)
    End Function
    
    Sub ccc()
    Dim d1 As Date, d2 As Date
    d1 = Date
    d2 = Date - 1
       MsgBox confronta(d1, d2, ">=")
    End Sub



  • di Zer0Kelvin data: 27/03/2014 22:46:41

    A prima vista, questa funziona
     
    Function confronta(V1 As Variant, V2 As Variant, Op As String) As Boolean
       confronta = Evaluate(V1 & Op & V2)
    End Function
    
    Function ContaSe(Elenco As Range, Operatore As String, Valore As Variant) As Long
    Dim C As Range
        ContaSe = 0
        For Each C In Elenco.Cells
          If confronta(C.Value, Valore, Operatore) Then ContaSe = ContaSe + 1
        Next C
    End Function
    



  • di scossa data: 27/03/2014 22:53:38

    cit. vmontal: "Function ContaSe(Elenco As Range, Operatore As String, Valore As Variant) As Integer"

    scusami, ma cosa c'azzecca questo codice con la tua richiesta originale, così come illustrata nell'esempio del tuo primo post

    Comunque l'udf sotto funziona perfettamente in Excel 2003:

    In A1:A10 dati vari
    =miocontase(A1:A5;"<";10)

    In E1:E15 date varie
    =miocontase(E1:E14;"<";DATA.VALORE("15/06/2000"))

    In H1:H4 dati vari
    =miocontase(H1:H4;"<=";"gamma")



     
    Public Function MioContaSe(rng As Range, sOper As String, vVal As Variant) As Long
      MioContaSe = Application.CountIf(rng, sOper & vVal)
    End Function
    
    Sub prova()
      MsgBox MioContaSe(Range("A1:A15"), "<", 10)
    End Sub
    



  • di vmontal data: 27/03/2014 23:46:33

    @ Scossa
    cit: vmontal "scrivere una Function a cui devo passare oltre a dei valori anche l'Operatore di confronto "

    ci azzecca perché la mia richiesta era appunto come passare ad un Function un "Operatore di Confronto"


    ... e poi non ho detto che le WorkSheetFunction non funzionano, ma semplicemente che sono inaffidabili; ho sperimentato più volte comportamenti anomali (non funzionano) su uno stesso codice su medesime condizioni! (liberi di non credere)!



  • di Zer0Kelvin data: 28/03/2014 06:48:18

    Resterebbe da capire "perchè" le WorkSheetFunction non ti funzionano; se le altre condizioni sono le stesse il problema è probabilmente dovuto ai dati passati alla funzione(per esempio, cerchi di contare delle date confrontandole con delle stringhe, o dei numeri che non hanno lo stesso numero di cifre decimali). L'unico problema con le WorkSheetFunction di cui sono a conoscenza, è che possono non avere lo stesso nome nelle varie versioni di Excel.



  • di vmontal data: 28/03/2014 07:58:13

    cit. Zer0Kelvin: "Resterebbe da capire "perchè" le WorkSheetFunction non ti funzionano".

    sono d'accordo! Ma questo sarà l'oggetto di un prossimo post.



  • di scossa data: 28/03/2014 10:39:25

    cit.: "ci azzecca perché la mia richiesta era appunto come passare ad un Function un "Operatore di Confronto" "

    Suvvia che hai capito bene cosa intendevo, perché se la tua richiesta fosse stata questa ti saresti già dato la risposta:
    Calcola(Valore1 as Integer, Valore2 as Integer, Operatore as String) as Integer

    Ma tu in realtà chiedevi non come *passare*, ma come *utilizzare* l'operatore passato.
    E l'esempio che avevi fatto, due singoli integer e un operatore per "assemblare" una funzione, poco c'azzeccava con la reale necessita: emulare una funzione nativa di Excel, e quindi l'esempio era fuorviante. Tutto qui, niente di grave

    cit.: "Ho sperimentato più volte comportamenti anomali (non funzionano) su uno stesso codice su medesime condizioni!"

    Secondo me (opinione personale) quelli che chiami comportamenti anomali sono semplicemente dovuti al fatto che usare una WorksheetFunction in VBA richiede una appropriata gestione degli eventuali errori restituiti dalla funzione chiamata; gestione che è diversa se si usa la forma estesa (ad esempio Application.WorksheetFunction.CountIf), oppure la forma contratta (Application.CountIf).