2014-07-08 10 views
12

VBA nicht kurzschlussVBA Kurzschluss `und` Alternativen

VBA nicht Kurzschlüsse unterstützen - offenbar, weil es nur bitweise And/Or/Nicht etc Operationen hat. Aus der VBA language specification: "Logische Operatoren sind einfache Datenoperatoren, die bitweise Berechnungen an ihren Operanden durchführen." In diesem Licht betrachtet, macht es Sinn, dass VBA mit true = &H1111 und false = &H0000 entworfen wurde: auf diese Weise logische Aussagen können als bitweise Operationen ausgewertet werden.

Der Mangel an Kurzschluss kann

  1. Leistungsprobleme verursachen: die ReallyExpensiveFunction() wird immer ausgeführt, wenn diese Aussage ausgewertet wird, auch wenn es durch das Ergebnis der auf der linken Seite nicht notwendig ist, die Bedingung

    If IsNecessary() And ReallyExpensiveFunction() Then '... End If

  2. Fehler: wenn MyObj Nichts, dieses bedingte statment wird in einem Laufzeitfehler ist Folge weil VBA noch den Wert von Property

    If Not MyObj Is Nothing And MyObj.Property = 5 Then '... End If

Die Lösung Ich werde versuchen, haben Kurz cirtcuiting Verhalten verwendet, um zu überprüfen zu implementieren verschachtelt ist If s

If cond1 And cond2 Then 
    '... 
End If 

Becomes
If cond1 Then 
    If cond2 Then 
     '... 
    End If 
End If 

Auf diese Weise der If-Anweisungen das Kurzschlussähnliches Verhalten gibt nicht cond2 wenn cond1 ist False zu bewerten stört.

Wenn es eine Else-Klausel ist, schafft dies doppelte Codeblocks

If Not MyObj Is Nothing And MyObj.Property = 5 Then 
    MsgBox "YAY" 
Else 
    MsgBox "BOO" 
End If 

Werden

If Not MyObj Is Nothing Then 
    If MyObj.Property = 5 Then 
     MsgBox "YAY" 
    Else 
     MsgBox "BOO" 'Duplicate 
    End If 
Else 
    MsgBox "BOO" 'Duplicate 
End If 

Gibt es eine Möglichkeit If Aussagen zu umschreiben das Kurzschlussverhalten zu erhalten, aber doppelte Code-Vervielfältigung vermeiden?

Vielleicht mit einer anderen Verzweigung Aussage wie Select Case?


Um Kontext zu der Frage hinzuzufügen, hier ist der spezifische Fall, den ich betrachte. Ich implementiere eine Hash-Tabelle, die Kollisionen behandelt, indem sie sie in einer verknüpften Liste verkettet. Die zugrunde liegende Array-Größe wird als Zweierpotenz erzwungen, und die Hashwerte werden auf die aktuelle Array-Größe verteilt, indem sie auf die entsprechende Länge gekürzt werden.

Angenommen, die Arraylänge ist 16 (binär 10000). Wenn ich einen Schlüssel mit 27 (binär 11011) habe, kann ich ihn in meinem 16-Slot-Array speichern, indem ich nur die Bits innerhalb des Limits dieser Array-Größe belasse.Der Index, in dem dieser Artikel gespeichert wird, ist (hash value) And (length of array - 1), was in diesem Fall (binary 11011) And (1111) ist, was 1011 ist, was 11 ist. Der tatsächliche Hash-Code wird zusammen mit dem Schlüssel im Slot gespeichert.

Beim Suchen eines Elements in der Hash-Tabelle in einer Kette müssen sowohl der Hash als auch der Schlüssel überprüft werden, um festzustellen, ob das richtige Element gefunden wurde. Wenn der Hash jedoch nicht übereinstimmt, besteht kein Grund, den Schlüssel zu überprüfen. Ich hatte gehofft, einige winzige immaterielle Menge an Leistung zu gewinnen, indem sie die Ifs Verschachtelung der Kurzschlussverhalten zu bekommen:

While Not e Is Nothing 
    If keyhash = e.hash Then 
     If Key = e.Key Then 
      e.Value = Value 
      Exit Property 
     Else 
      Set e = e.nextEntry 
     End If 
    Else 
     Set e = e.nextEntry 
    End If 
Wend 

Sie sehen die Set... dupliziert und somit diese Frage.

+3

Vielleicht eine naive Frage, aber können Sie nicht die 'SET' Zeile außerhalb der IFS (und innerhalb der Weile vor der unteren Zeile) bewegen? Sie "SET" nicht nur beim Beenden, sonst setzen Sie. Gute Frage übrigens! – Ioannis

+0

@Ioannis - (Hand auf den Kopf klatschen). Bitte stelle das als Antwort auf. :) – hnk

+0

@loannis Ich kann das nicht tun, es macht WAAAAY zu viel Sinn;) Danke für das Aufzeigen der richtigen Lösung für mein spezifisches Problem, die Änderung an meinem Code jetzt machen ... Ich werde den Rest des lassen Frage allein für die Nachwelt stehen. – Blackhawk

Antwort

9

Als allgemeinere apprach, schlage ich vor Zustand Flaggen einzuführen und die Verwendung der Zuordnung von Vergleichsergebnissen zu booleans machen:

dim cond1 as boolean 
dim cond2 as boolean 

cond1 = false 
cond2 = false 

' Step 1 
cond1 = MyObj Is Nothing 

' Step 2: do it only if step 1 was sucessful 
if cond1 then 
    cond2 = MyObj.Property = 5 
end if 

' Final result: 
if cond2 then 
    msgbox "Yay" 
else 
    msgbox "Boo" 
end if 

Durch diese Bedingung Flags „Chaining“, jeder Schritt ist sicher, sehen Sie die endgültige Ergebnis in der letzten Bedingung Flagge und Sie nicht unnötige Vergleiche. Und für mich bleibt es lesbar.

EDIT 14.09.07

ich in der Regel verzichtet werden nie delimiters Block und ich folglich jede Aussage von Kontrollstrukturen auf einer neuen Zeile gesetzt. Aber in diesem Fall können Sie sorgfältig eine sehr dichte Schreibweise erhalten, die auf Kurzschluss Notation erinnert, auch weil die VBA-Compiler die Variablen initiiert:

dim cond1 as boolean 
dim cond2 as boolean 
dim cond3 as boolean 
dim cond4 as boolean 

cond1 = MyObj Is Nothing 
if cond1 then cond2 = MyObj.Property = 5 
if cond2 then cond3 = MyObj.Property2 = constSomething 
if cond3 then cond4 = not isNull(MyObj.Property77) 

if cond4 then 
    msgbox "Hyper-Yay" 
else 
    msgbox "Boo" 
end if 

ich zustimmen könnte. Es ist ein klarer Fluss zum Lesen.

+0

Ich mag das. Bedingungsflags, manchmal verschachtelte "Ifs", Funktionen und Subroutinen, die Argumente 'ByRef' anstelle von' ByVal' usw. verwenden, gibt es viele Möglichkeiten, dies zu umgehen, aber sie hängen alle von den besonderen Umständen ab. –

+2

Danke. Ich stimme zu, dass es noch mehr Ansätze gibt und das hängt von der Situation ab. Hmmm ... Auch eine generische 'Funktion IfShortcut (ParamArray conditions()) als boolean ...' wäre nett ... In diesem Zustand kann man loopen und 'exit'. –

+0

Hmmm ... Irgendwie muss man den Endzustand überprüfen. Sie könnten das letzte Argument wie Tim Burton vorbereiten und versuchen, ein if zu vermeiden. Aber das Muster erfordert eine abschließende Überprüfung. –

0

Wie wäre:

s = "BOO" 

If Not MyObj Is Nothing Then 
    If MyObj.Property = 5 Then s = "YAY" 
End If 

MsgBox s 
+0

Aber "BOO" könnte die "teure" Funktion sein, die Sie möglichst vermeiden möchten. Würde es für diesen Fall eine alternative Problemumgehung geben? – hnk

+0

Unglücklicherweise habe ich es mit der Else-Klausel zu tun, die eigentlich eine 'Set'-Zuweisung hat :(Trotzdem würde so etwas funktionieren, aber ich bin mir nicht sicher, ob es den winzigen Leistungsgewinn aufheben würde Ich werde die Frage modifizieren und den spezifischen Fall für den Kontext hinzufügen. Sehen Sie, was Sie denken! – Blackhawk

+0

Ich weiß, dass das wirklich alt ist, aber für zukünftige Leser ... alles, was hier notwendig wäre um die teure Funktion zu vermeiden, sowie die unordentlichere Version (z. B. GoTo) wird nach der Nullversion der Variablen gesucht, die wir versuchen zu machen. In diesem Fall 'If s = vbNullString Dann s = ExpensiveFunction'. Sauber und logisch. –

4

Es ist ein Weg. Du wirst es nicht mögen. Aber das ist eine jener sorgfältig konstruierten Fällen, in denen Goto praktisch

If Not MyObj Is Nothing Then 
    If MyObj.Property = 5 Then 
     MsgBox "YAY" 
    Else 
     Goto JUMPHERE 
    End If 
Else 
JUMPHERE: 
    MsgBox "BOO" 'Duplicate 
End If 
kommt

Ein kurzgeschlossen Code, um einen kurzgeschlossenen Zustand zu implementieren!

Alternativ, wenn anstelle von MsgBox "BOO" ist einige lange und gewundenen Code, kann es in eine Funktion verpackt werden, und das kann zweimal mit minimalen Auswirkungen/Overhead geschrieben werden.


Im Hinblick auf die spezifischen Anwendungsfall haben die mehrere Set Operationen eine minimale Auswirkung auf die Leistung und somit, wenn man Goto (noch weise die global effizienter Ansatz, Codegröße + Leistung vermeiden will Verwendung Schaffung vermeiden Dummy-Variablen usw. - spielt keine Rolle, obwohl für solch ein kleines Stück Code) es vernachlässigbar ist, den Befehl einfach zu wiederholen.

Just (Ihr Beispielcode) zu analysieren, wie viel kann durch unterschiedliche Methoden gewonnen werden ...

  • Wenn beide Bedingungen erfüllt sind: gibt es 2 Vergleiche, 1-Zuordnung, 0 springt
  • Wenn nur die erste Bedingung erfüllt ist: gibt es 2-Vergleiche, 1 Zeigerzuordnung, 1 jump
  • Wenn nur die zweite Bedingung erfüllt ist: ist mit 1 Vergleich 1 Zeigerzuordnung, 1 jump
  • Wenn beide Bedingungen falsch sind: ist mit 1 Vergleich 1 Zeigerzuweisung, 1 jump (wie oben)

In Bezug auf Leistung, ist ein Sprung in der Regel teurer als Vergleich (was passiert, sehr schnell in ALU gegen den Sprung, der zu einer Störung im Code-Cache führen könnte, vielleicht nicht bei diesen Größen, aber immer noch sind Sprünge teuer).

Und normale Zuordnung von Wert wäre bestenfalls so schnell wie eine Zeiger-Zuordnung oder manchmal schlimmer sein (diese VBA ist, kann nicht 100% sicher, der p-Code-Implementierung sein)

Also, je nach In diesem Fall können Sie versuchen, die durchschnittliche Anzahl der Sprünge pro Iteration in der Schleife zu minimieren und den Code neu zu ordnen.

+1

Funktionswrapper +1.Ich benutze sie (vielleicht übermäßig) aber es hilft wirklich halten Sie Ihren Code lesbar und einfacher zu pflegen. –

+2

+1 "Sie werden nicht garantiert, es zu mögen": P Dies würde definitiv lösen, aber ja das 'Goto' le Aves mir viel Platz, um meinen Code zu schrauben, wenn ich nicht vorsichtig bin. Könnte das Springen von Verzweigungen oder Schleifenaussagen jemals Probleme verursachen, die man denkt? – Blackhawk

+1

Nein, der C-Code von C++/VBA usw. konvertiert Ihre Zweige in Goto anyways, so dass es kein Implementierungsproblem gibt. Nur ein philosophischer. Aber das Argument (hauptsächlich gegen Djikstra) gegen Goto war vom Standpunkt der Vermeidung von Spaghetti-Code. So wie ich es für jede Optimierung mache, schreibe ich zuerst den ordentlichen Code. Dann kommentieren Sie alles und legen Sie den Hyperoptimierten daneben. Lesen Sie auf diese Weise den regulären Code für Logik und Debugging, und optimieren Sie ihn für die tatsächliche Leistung. Gerade in diesem Fall macht Goto das Lesen leichter - entpackt den Code! – hnk