Auswerten einer Reihe einfacher mathematischer Ausdrücke [geschlossen]

76

Herausforderung

Hier ist die Herausforderung (meiner eigenen Erfindung, obwohl ich mich nicht wundern würde, wenn sie zuvor an anderer Stelle im Internet erschienen wäre).

Schreiben Sie eine Funktion, die ein einzelnes Argument, das eine Zeichenfolgendarstellung eines einfachen mathematischen Ausdrucks ist, als Gleitkommawert auswertet. Ein "einfacher Ausdruck" kann eine der folgenden Angaben enthalten: positive oder negative Dezimalzahlen, + , - , * , / , ( , ) . Ausdrücke verwenden die (normale) Infixnotation . Bediener sollten in der Reihenfolge bewertet werden, in der sie erscheinen, dh nicht wie in BODMAS , obwohl Klammern natürlich korrekt beachtet werden sollten. Die Funktion sollte für jedes das richtige Ergebnis zurückgebenmöglicher Ausdruck dieser Form. Die Funktion muss jedoch keine fehlerhaften Ausdrücke verarbeiten (dh solche mit schlechter Syntax).

Beispiele für Ausdrücke:

1 + 3 / -8                            = -0.5       (No BODMAS)
2*3*4*5+99                            = 219
4 * (9 - 4) / (2 * 6 - 2) + 8         = 10
1 + ((123 * 3 - 69) / 100)            = 4
2.45/8.5*9.27+(5*0.0023)              = 2.68...

Regeln

Ich erwarte hier irgendeine Form von "Betrug" / List, also lassen Sie mich bitte davor warnen! Unter Betrug verstehe ich die Verwendung der evaloder einer gleichwertigen Funktion in dynamischen Sprachen wie JavaScript oder PHP oder das gleichzeitige Kompilieren und Ausführen von Code im laufenden Betrieb. (Ich denke, meine Spezifikation von "no BODMAS" hat dies jedoch so ziemlich garantiert.) Abgesehen davon gibt es keine Einschränkungen. Ich erwarte hier einige Regex-Lösungen, aber es wäre schön, mehr als nur das zu sehen.

Jetzt bin ich hauptsächlich an einer C # /. NET-Lösung interessiert, aber jede andere Sprache wäre auch vollkommen akzeptabel (insbesondere F # und Python für die funktionalen / gemischten Ansätze). Ich habe noch nicht entschieden, ob ich die kürzeste oder genialste Lösung (zumindest für die Sprache) als Antwort akzeptieren werde, aber ich würde jede Form von Lösung in jeder Sprache begrüßen , außer dem, was ich gerade oben verboten habe !

Meine Lösung

Ich habe jetzt meine C # -Lösung hier veröffentlicht (403 Zeichen). Update: Meine neue Lösung hat die alte mit 294 Zeichen mit Hilfe eines schönen Regex deutlich übertroffen! Ich hatte den Verdacht, dass dies von einigen der Sprachen mit leichterer Syntax (insbesondere der funktionalen / dynamischen) leicht übertroffen wird, und habe mich als richtig erwiesen, aber ich wäre neugierig, ob jemand dies in C # noch schlagen könnte.

Aktualisieren

Ich habe bereits einige sehr clevere Lösungen gesehen. Vielen Dank an alle, die einen gepostet haben. Obwohl ich noch keinen von ihnen getestet habe, werde ich den Leuten vertrauen und davon ausgehen, dass sie zumindest mit allen angegebenen Beispielen arbeiten.

Nur für den Hinweis, Wiedereintritt (dh Thread-Sicherheit) ist keine Voraussetzung für die Funktion, obwohl es ein Bonus ist.


Format

Bitte posten Sie alle Antworten zum leichteren Vergleich im folgenden Format:

Sprache

Anzahl von Charakteren: ???

Vollständig verschleierte Funktion:

(code here)

Klare / halbverschleierte Funktion:

(code here)

Alle Hinweise zum Algorithmus / clevere Verknüpfungen.


Noldorin
quelle
2
Sie haben wahrscheinlich gemeint, dass Ihr erstes Beispiel gleich 0,125 ist (Dezimalstelle verschieben) und Ihr zweites Beispiel 99 auf der linken Seite (eine zu viele Neunen).
John Y
Ja Danke. Das waren ziemlich krasse Tippfehler.
Noldorin
1
Sie sollten ein Beispiel hinzufügen, bei dem der Mangel an BODMAS signifikant ist, z. B. "1 + 1 * 3 = 6"
Ben Blank
3
Ahh, ich habe mich gefragt, wann die erste Abstimmung zum Abschluss kommen würde. Hinweis für alle Wähler: In StackOverflow gibt es bereits viele Open-Code-Golffragen. Konsens scheint zu sein, dass sie in Ordnung sind - hauptsächlich nur ein bisschen Spaß.
Noldorin
3
Ich bin geneigt zuzustimmen, dass dies in Ordnung ist, insbesondere als "Wiki"
Marc Gravell

Antworten:

36

Perl (keine Bewertung)

Anzahl der Zeichen: 167 106 (siehe unten für die 106-Zeichen-Version)

Vollständig verschleierte Funktion: (167 Zeichen, wenn Sie diese drei Zeilen zu einer verbinden)

sub e{my$_="($_[0])";s/\s//g;$n=q"(-?\d++(\.\d+)?+)";
@a=(sub{$1},1,sub{$3*$6},sub{$3+$6},4,sub{$3-$6},6,sub{$3/$6});
while(s:\($n\)|(?<=\()$n(.)$n:$a[7&ord$5]():e){}$_}

Klare / deobfuscated Version:

sub e {
  my $_ = "($_[0])";
  s/\s//g;
  $n=q"(-?\d++(\.\d+)?+)"; # a regex for "number", including capturing groups
                           # q"foo" in perl means the same as 'foo'
                           # Note the use of ++ and ?+ to tell perl
                           # "no backtracking"

  @a=(sub{$1},             # 0 - no operator found
      1,                   # placeholder
      sub{$3*$6},          # 2 - ord('*') = 052
      sub{$3+$6},          # 3 - ord('+') = 053
      4,                   # placeholder
      sub{$3-$6},          # 5 - ord('-') = 055
      6,                   # placeholder
      sub{$3/$6});         # 7 - ord('/') = 057

  # The (?<=... bit means "find a NUM WHATEVER NUM sequence that happens
  # immediately after a left paren", without including the left
  # paren.  The while loop repeatedly replaces "(" NUM WHATEVER NUM with
  # "(" RESULT and "(" NUM ")" with NUM.  The while loop keeps going
  # so long as those replacements can be made.

  while(s:\($n\)|(?<=\()$n(.)$n:$a[7&ord$5]():e){}

  # A perl function returns the value of the last statement
  $_
}

Ich hatte die Regeln anfangs falsch verstanden, also hatte ich eine Version mit "eval" eingereicht. Hier ist eine Version ohne.

Die neueste wenig Einsicht kam , als ich erkennen , dass der letzte Oktalziffer in dem Zeichencodes für +, -, /, und *ist anders, Und das ord(undef)ist 0. Auf diese Weise können Sie mir die Dispatch - Tabelle aufgebaut @aals ein Array, und nur invoke den Code bei der Lage 7 & ord($3).

Es gibt einen offensichtlichen Punkt, an dem Sie einen weiteren Charakter rasieren können - wechseln Sie q""in ''-, aber das würde das Ausschneiden und Einfügen in die Shell erschweren.

Noch kürzer

Anzahl der Zeichen: 124 106

Unter Berücksichtigung der Änderungen durch Ephemient sind es jetzt nur noch 124 Zeichen: (Verbinden Sie die beiden Zeilen zu einer)

sub e{$_=$_[0];s/\s//g;$n=q"(-?\d++(\.\d+)?+)";
1while s:\($n\)|$n(.)$n:($1,1,$3*$6,$3+$6,4,$3-$6,6,$6&&$3/$6)[7&ord$5]:e;$_}

Noch kürzer

Anzahl der Zeichen: 110 106

Die Rubinlösung unten treibt mich weiter, obwohl ich ihre 104 Zeichen nicht erreichen kann:

sub e{($_)=@_;$n='( *-?[.\d]++ *)';
s:\($n\)|$n(.)$n:(($1,$2-$4,$4&&$2/$4,$2*$4,$2+$4)x9)[.8*ord$3]:e?e($_):$_}

Ich musste nachgeben und benutzen ''. Dieser Rubin- sendTrick ist wirklich nützlich für dieses Problem.

Wasser aus einem Stein drücken

Anzahl der Zeichen: 106

Eine kleine Verzerrung, um die Überprüfung durch Null zu vermeiden.

sub e{($_)=@_;$n='( *-?[.\d]++ *)';
s:\($n\)|$n(.)$n:($1,0,$2*$4,$2+$4,0,$2-$4)[7&ord$3]//$2/$4:e?e($_):$_}

Hier ist das Testkabel für diese Funktion:

perl -le 'sub e{($_)=@_;$n='\''( *-?[.\d]++ *)'\'';s:\($n\)|$n(.)$n:($1,0,$2*$4,$2+$4,0,$2-$4)[7&ord$3]//$2/$4:e?e($_):$_}' -e 'print e($_) for @ARGV' '1 + 3' '1 + ((123 * 3 - 69) / 100)' '4 * (9 - 4) / (2 * 6 - 2) + 8' '2*3*4*5+99' '2.45/8.5*9.27+(5*0.0023) ' '1 + 3 / -8'
Daniel Martin
quelle
3
Es ist ziemlich beängstigend, wie klein Perl sein kann. Ich habe meine Antwort bearbeitet, um die kleinste Ruby-Implementierung beizubehalten, und bei 170 Zeichen ist kein Platz mehr vorhanden. Aber 124? Gute Soße!
Robert K
1
Ich habe nicht bemerkt, dass es noch niemand erwähnt hat, aber diese Lösung erfordert Perl 5.10. Verwenden Sie zur Kompatibilität mit 5.8 (-? (?> \ D + (\. \ D +)?)), Das zwei Zeichen länger ist.
Ephemient
7
@Epaga, mach dir keine Sorgen, ich habe deinen Tippfehler: Perl. ist. genial.
Danny
1
Kürzen Sie es um 1 Zeichen - ändern Sie "$ _ = $ _ [0]" in "($ _) = @ _".
Chris Lutz
1
Da die Arithmetik unabhängig vom Operator unbedingt ausgeführt wird (das richtige Ergebnis wird später ausgewählt), muss die Division durch Null vermieden werden.
Ephemient
44

Assembler

427 Bytes

Verschleiert, zusammengebaut mit dem exzellenten A86 zu einer ausführbaren .com-Datei:

dd 0db9b1f89h, 081bee3h, 0e8af789h, 0d9080080h, 0bdac7674h, 013b40286h
dd 07400463ah, 0ccfe4508h, 08ce9f675h, 02fc8000h, 013b0057eh, 0feaac42ah
dd 0bedf75c9h, 0ba680081h, 04de801h, 04874f73bh, 04474103ch, 0e8e8b60fh
dd 08e8a003fh, 0e880290h, 0de0153h, 08b57e6ebh, 0d902a93eh, 046d891dh
dd 08906c783h, 05f02a93eh, 03cffcee8h, 057197510h, 02a93e8bh, 08b06ef83h
dd 05d9046dh, 02a93e89h, 03bc9d95fh, 0ac0174f7h, 074f73bc3h, 0f3cac24h
dd 0eed9c474h, 0197f0b3ch, 07cc4940fh, 074f73b09h, 0103cac09h, 0a3ce274h
dd 0e40a537eh, 0e0d90274h, 02a3bac3h, 021cd09b4h, 03e8b20cdh, 0ff8102a9h
dd 0ed7502abh, 0474103ch, 0e57d0b3ch, 0be02a3bfh, 014d903a3h, 0800344f6h
dd 02db00574h, 0d9e0d9aah, 0d9029f2eh, 0bb34dfc0h, 08a0009h, 01c75f0a8h
dd 020750fa8h, 0b0f3794bh, 021e9aa30h, 0de607400h, 08802990eh, 0de07df07h
dd 0c392ebc1h, 0e8c0008ah, 0aa300404h, 0f24008ah, 04baa3004h, 02eb0ee79h
dd 03005c6aah, 0c0d90ab1h, 0e9defcd9h, 02a116deh, 0e480e0dfh, 040fc8045h
dd 0ede1274h, 0c0d90299h, 015dffcd9h, 047300580h, 0de75c9feh, 0303d804fh
dd 03d80fa74h, 04f01752eh, 0240145c6h, 0dfff52e9h, 0d9029906h, 0f73b025fh
dd 03caca174h, 07fed740ah, 0df07889ah, 0277d807h, 047d9c1deh, 0990ede02h
dd 025fd902h, 03130e0ebh, 035343332h, 039383736h, 02f2b2d2eh, 02029282ah
dd 0e9000a09h, 07fc9f9c1h, 04500000fh, 0726f7272h
db 024h, 0abh, 02h

EDIT: Unverschmierte Quelle:

        mov [bx],bx
        finit
        mov si,81h
        mov di,si
        mov cl,[80h]
        or cl,bl
        jz ret
    l1:
        lodsb
        mov bp,d1
        mov ah,19
    l2:
        cmp al,[bp]
        je l3
        inc bp
        dec ah
        jne l2
        jmp exit
    l3:
        cmp ah,2
        jle l4
        mov al,19
        sub al,ah
        stosb
    l4:
        dec cl
        jnz l1
        mov si,81h
        push done

    decode:
    l5:
        call l7
    l50:
        cmp si,di
        je ret
        cmp al,16
        je ret
        db 0fh, 0b6h, 0e8h ; movzx bp,al
        call l7
        mov cl,[bp+op-11]
        mov byte ptr [sm1],cl
        db 0deh
    sm1:db ?
        jmp l50

    open:
        push di
        mov di,word ptr [s]
        fstp dword ptr [di]
        mov [di+4],bp
        add di,6
        mov word ptr [s],di
        pop di
        call decode
        cmp al,16
        jne ret
        push di
        mov di,word ptr [s]
        sub di,6
        mov bp,[di+4]
        fld dword ptr [di]
        mov word ptr [s],di
        pop di
        fxch st(1)
        cmp si,di
        je ret
        lodsb
        ret



    l7: cmp si,di
        je exit
        lodsb
        cmp al,15
        je open
        fldz
        cmp al,11
        jg exit
        db 0fh, 94h, 0c4h ; sete ah 
        jl l10
    l9:
        cmp si,di
        je l12
        lodsb
        cmp al,16
        je ret
    l10:
        cmp al,10
        jle l12i

    l12:
        or ah,ah
        je l13
        fchs
    l13:
        ret

    exit:
        mov dx,offset res
        mov ah,9
        int 21h
        int 20h

    done:
        mov di,word ptr [s]
        cmp di,(offset s)+2
        jne exit
        cmp al,16
        je ok
        cmp al,11
        jge exit
    ok:
        mov di,res
        mov si,res+100h
        fst dword ptr [si]
        test byte ptr [si+3],80h
        jz pos
        mov al,'-'
        stosb
        fchs
    pos:
        fldcw word ptr [cw]
        fld st(0)
        fbstp [si]
        mov bx,9
    l1000:
        mov al,[si+bx]
        test al,0f0h
        jne startu
        test al,0fh
        jne startl
        dec bx
        jns l1000
        mov al,'0'
        stosb
        jmp frac

    l12i:
        je l11
        fimul word ptr [d3]
        mov [bx],al
        fild word ptr [bx]
        faddp
        jmp l9
        ret

    startu:
        mov al,[si+bx]
        shr al,4
        add al,'0'
        stosb
    startl:
        mov al,[si+bx]
        and al,0fh
        add al,'0'
        stosb
        dec bx
        jns startu

    frac:
        mov al,'.'
        stosb
        mov byte ptr [di],'0'
        mov cl,10
        fld st(0)
        frndint
    frac1:  
        fsubp st(1)
        ficom word ptr [zero]
        fstsw ax
        and ah,045h
        cmp ah,040h
        je finished
        fimul word ptr [d3]
        fld st(0)
        frndint
        fist word ptr [di]
        add byte ptr [di],'0'
        inc di
        dec cl
        jnz frac1

    finished:   
        dec di
        cmp byte ptr [di],'0'
        je finished
        cmp byte ptr [di],'.'
        jne f2
        dec di
    f2:
        mov byte ptr [di+1],'$'
    exit2:
        jmp exit


    l11:
        fild word ptr [d3]
        fstp dword ptr [bx+2]
    l111:
        cmp si,di
        je ret
        lodsb
        cmp al,10
        je exit2
        jg ret
        mov [bx],al
        fild word ptr [bx]
        fdiv dword ptr [bx+2]
        faddp
        fld dword ptr [bx+2]
        fimul word ptr [d3]
        fstp dword ptr [bx+2]
        jmp l111


    d1: db '0123456789.-+/*()', 32, 9
    d3: dw 10
    op: db 0e9h, 0c1h, 0f9h, 0c9h
    cw: dw 0f7fh
    zero: dw 0
    res:db 'Error$'
    s:  dw (offset s)+2
Skizz
quelle
1
Montage - das ist echte Programmierung!
Andreas Rejbrand
Ich habe einmal ein komplettes Tetris-Spiel in 64 Bytes gesehen
BlueRaja - Danny Pflughoeft
29

Rubin

Anzahl der Zeichen: 103

N='( *-?[\d.]+ *)'
def e x
x.sub!(/\(#{N}\)|#{N}([^.\d])#{N}/){$1or(e$2).send$3,e($4)}?e(x):x.to_f
end

Dies ist eine nicht rekursive Version der Lösung von The Wicked Flea. Unterausdrücke in Klammern werden von unten nach oben statt von oben nach unten ausgewertet.

Bearbeiten : Durch das Konvertieren des 'while' in eine bedingte + Schwanzrekursion wurden einige Zeichen gespeichert, sodass es nicht mehr nicht rekursiv ist (obwohl die Rekursion semantisch nicht erforderlich ist).

Bearbeiten : Das Ausleihen von Daniel Martins Idee, die regulären Ausdrücke zusammenzuführen, spart weitere 11 Zeichen!

Edit : Diese Rekursion ist noch nützlicher als ich zuerst dachte! x.to_fkann wie folgt umgeschrieben werden e(x), wenn xzufällig eine einzelne Nummer enthalten ist.

Bearbeiten : Wenn Sie ' or' anstelle von ' ||' verwenden, können zwei Klammern entfernt werden.

Lange Version:

# Decimal number, as a capturing group, for substitution
# in the main regexp below.
N='( *-?[\d.]+ *)'

# The evaluation function
def e(x)
  matched = x.sub!(/\(#{N}\)|#{N}([^\d.])#{N}/) do
    # Group 1 is a numeric literal in parentheses.  If this is present then
    # just return it.
    if $1
      $1
    # Otherwise, $3 is an operator symbol and $2 and $4 are the operands
    else
      # Recursively call e to parse the operands (we already know from the
      # regexp that they are numeric literals, and this is slightly shorter
      # than using :to_f)
      e($2).send($3, e($4))
      # We could have converted $3 to a symbol ($3.to_s) or converted the
      # result back to string form, but both are done automatically anyway
    end
  end
  if matched then
    # We did one reduction. Now recurse back and look for more.
    e(x)
  else
    # If the string doesn't look like a non-trivial expression, assume it is a
    # string representation of a real number and attempt to parse it
    x.to_f
  end
end
finnw
quelle
Ich hätte fast gedacht, dass dies der neue Anführer ist, bis ich sah, dass der Perl bearbeitet wurde, um noch kürzer zu werden! Jedenfalls gute Arbeit.
Noldorin
1
Wenn Sie 'e = readline.chomp; ...; p e.to_f' loswerden und 'def q (e); ...; e.to_f; end' wie bei den anderen Lösungen verwenden, werden 10 Zeichen eingespart. Es wird jedoch nicht wie in der Frage q ("1 + 3 / -8") == - 0,5 angegeben.
Ephemient
@ephemient, das ist ein Fehler, den Sie gefunden haben - er konnte keine negativen Zahlen verarbeiten.
Finnw
1
Das gsub! ('-', '') in meinem Code dient dazu, wie das Argument in Klammern funktioniert, wenn es negiert wird. Wenn das Ergebnis des Inneren einer negierten Klammer negativ ist, bleibt das Minus außerhalb der Aussage: --7.0, zum Beispiel. Die Unterstützung kostet mich jedoch 24 Zeichen, immer noch 19 über Ihnen. Ich weiß nicht, dass ich es mehr schrumpfen kann als die Tricks, die ich von dir gelernt habe. (Aber ich habe großartig für einen zweiten Versuch gemacht!)
Robert K
1
Die Verwendung von "send" kommt der Verletzung der "no eval" -Regel sehr nahe. Aber netter Trick, der die Leerzeichen in Ihren Zahlenregex einbezieht. Mit diesem und einem anderen Trick konnte ich meine Perl-Lösung auf 119 Zeichen reduzieren.
Daniel Martin
29

C (VS2005)

Anzahl der Zeichen: 1360

Missbrauch des Präprozessors und Warnungen für ein lustiges Code-Layout (scrollen Sie nach unten, um zu sehen):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define b main
#define c(a) b(a,0)
#define d -1
#define e -2
#define g break
#define h case
#define hh h
#define hhh h
#define w(i) case i
#define i return
#define j switch
#define k float
#define l realloc
#define m sscanf
#define n int _
#define o char
#define t(u) #u
#define q(r) "%f" t(r)  "n"
#define s while
#define v default
#define ex exit
#define W printf
#define x fn()
#define y strcat
#define z strcpy
#define Z strlen

char*p    =0    ;k    *b    (n,o**    a){k*f
;j(_){    hh   e:     i*    p==40?    (++p,c
(d        ))  :(      f=        l(        0,
4)        ,m (p       ,q        (%        ),
f,&_),    p+=_        ,f       );        hh
d:f=c(    e);s        (1      ){        j(
    *p    ++ ){       hh     0:        hh
    41    :i  f;      hh    43        :*
f+=*c(    e)   ;g     ;h    45:*f=    *f-*c(
e);g;h    42    :*    f=    *f**c(    e);g;h

47:*f      /=*c      (e);     g;   v:    c(0);}
}w(1):    if(p&&    printf    (q  ((     "\\"))
,*  c(    d)  ))    g;  hh    0: ex      (W
(x  ))    ;v  :p    =(        p?y:       z)(l(p
,Z(1[     a]  )+    (p        ?Z(p           )+
1:1))     ,1  [a    ])  ;b    (_ -1          ,a
+1  );    g;  }i    0;};fn    ()  {n     =42,p=
43  ;i     "Er"      "ro"     t(   r)    "\n";}
Skizz
quelle
25

Visual Basic.NET

Anzahl der Zeichen: 9759

Ich bin selbst eher ein Bowler.

HINWEIS: Berücksichtigt keine verschachtelten Klammern. Auch ungetestet, aber ich bin mir ziemlich sicher, dass es funktioniert.

Imports Microsoft.VisualBasic
Imports System.Text
Imports System.Collections.Generic
Public Class Main
Public Shared Function DoArithmaticFunctionFromStringInput(ByVal MathematicalString As String) As Double
    Dim numberList As New List(Of Number)
    Dim operationsList As New List(Of IOperatable)
    Dim currentNumber As New Number
    Dim currentParentheticalStatement As New Parenthetical
    Dim isInParentheticalMode As Boolean = False
    Dim allCharactersInString() As Char = MathematicalString.ToCharArray
    For Each mathChar In allCharactersInString
        If mathChar = Number.ZERO_STRING_REPRESENTATION Then
            currentNumber.UpdateNumber(mathChar)
        ElseIf mathChar = Number.ONE_STRING_REPRESENTATION Then
            currentNumber.UpdateNumber(mathChar)
        ElseIf mathChar = Number.TWO_STRING_REPRESENTATION Then
            currentNumber.UpdateNumber(mathChar)
        ElseIf mathChar = Number.THREE_STRING_REPRESENTATION Then
            currentNumber.UpdateNumber(mathChar)
        ElseIf mathChar = Number.FOUR_STRING_REPRESENTATION Then
            currentNumber.UpdateNumber(mathChar)
        ElseIf mathChar = Number.FIVE_STRING_REPRESENTATION Then
            currentNumber.UpdateNumber(mathChar)
        ElseIf mathChar = Number.SIX_STRING_REPRESENTATION Then
            currentNumber.UpdateNumber(mathChar)
        ElseIf mathChar = Number.SEVEN_STRING_REPRESENTATION Then
            currentNumber.UpdateNumber(mathChar)
        ElseIf mathChar = Number.EIGHT_STRING_REPRESENTATION Then
            currentNumber.UpdateNumber(mathChar)
        ElseIf mathChar = Number.NINE_STRING_REPRESENTATION Then
            currentNumber.UpdateNumber(mathChar)
        ElseIf mathChar = Number.DECIMAL_POINT_STRING_REPRESENTATION Then
            currentNumber.UpdateNumber(mathChar)
        ElseIf mathChar = Addition.ADDITION_STRING_REPRESENTATION Then
            Dim addition As New Addition

            If Not isInParentheticalMode Then
                operationsList.Add(addition)
                numberList.Add(currentNumber)
            Else
                currentParentheticalStatement.AllNumbers.Add(currentNumber)
                currentParentheticalStatement.AllOperators.Add(addition)
            End If

            currentNumber = New Number
        ElseIf mathChar = Number.NEGATIVE_NUMBER_STRING_REPRESENTATION Then
            If currentNumber.StringOfNumbers.Length > 0 Then
                currentNumber.UpdateNumber(mathChar)

                Dim subtraction As New Addition
                If Not isInParentheticalMode Then
                    operationsList.Add(subtraction)
                    numberList.Add(currentNumber)
                Else
                    currentParentheticalStatement.AllNumbers.Add(currentNumber)
                    currentParentheticalStatement.AllOperators.Add(subtraction)
                End If

                currentNumber = New Number
            Else
                currentNumber.UpdateNumber(mathChar)
            End If
        ElseIf mathChar = Multiplication.MULTIPLICATION_STRING_REPRESENTATION Then
            Dim multiplication As New Multiplication

            If Not isInParentheticalMode Then
                operationsList.Add(multiplication)
                numberList.Add(currentNumber)
            Else
                currentParentheticalStatement.AllNumbers.Add(currentNumber)
                currentParentheticalStatement.AllOperators.Add(multiplication)
            End If
            currentNumber = New Number
        ElseIf mathChar = Division.DIVISION_STRING_REPRESENTATION Then
            Dim division As New Division

            If Not isInParentheticalMode Then
                operationsList.Add(division)
                numberList.Add(currentNumber)
            Else
                currentParentheticalStatement.AllNumbers.Add(currentNumber)
                currentParentheticalStatement.AllOperators.Add(division)
            End If
            currentNumber = New Number
        ElseIf mathChar = Parenthetical.LEFT_PARENTHESIS_STRING_REPRESENTATION Then
            isInParentheticalMode = True
        ElseIf mathChar = Parenthetical.RIGHT_PARENTHESIS_STRING_REPRESENTATION Then
            currentNumber = currentParentheticalStatement.EvaluateParentheticalStatement
            numberList.Add(currentNumber)
            isInParentheticalMode = False
        End If
    Next

    Dim result As Double = 0
    Dim operationIndex As Integer = 0
    For Each numberOnWhichToPerformOperations As Number In numberList
        result = operationsList(operationIndex).PerformOperation(result, numberOnWhichToPerformOperations)
        operationIndex = operationIndex + 1
    Next

    Return result

End Function
Public Class Number
    Public Const DECIMAL_POINT_STRING_REPRESENTATION As Char = "."
    Public Const NEGATIVE_NUMBER_STRING_REPRESENTATION As Char = "-"
    Public Const ZERO_STRING_REPRESENTATION As Char = "0"
    Public Const ONE_STRING_REPRESENTATION As Char = "1"
    Public Const TWO_STRING_REPRESENTATION As Char = "2"
    Public Const THREE_STRING_REPRESENTATION As Char = "3"
    Public Const FOUR_STRING_REPRESENTATION As Char = "4"
    Public Const FIVE_STRING_REPRESENTATION As Char = "5"
    Public Const SIX_STRING_REPRESENTATION As Char = "6"
    Public Const SEVEN_STRING_REPRESENTATION As Char = "7"
    Public Const EIGHT_STRING_REPRESENTATION As Char = "8"
    Public Const NINE_STRING_REPRESENTATION As Char = "9"

    Private _isNegative As Boolean
    Public ReadOnly Property IsNegative() As Boolean
        Get
            Return _isNegative
        End Get
    End Property
    Public ReadOnly Property ActualNumber() As Double
        Get
            Dim result As String = ""
            If HasDecimal Then
                If DecimalIndex = StringOfNumbers.Length - 1 Then
                    result = StringOfNumbers.ToString
                Else
                    result = StringOfNumbers.Insert(DecimalIndex, DECIMAL_POINT_STRING_REPRESENTATION).ToString
                End If
            Else
                result = StringOfNumbers.ToString
            End If
            If IsNegative Then
                result = NEGATIVE_NUMBER_STRING_REPRESENTATION & result
            End If
            Return CType(result, Double)
        End Get
    End Property
    Private _hasDecimal As Boolean
    Public ReadOnly Property HasDecimal() As Boolean
        Get
            Return _hasDecimal
        End Get
    End Property
    Private _decimalIndex As Integer
    Public ReadOnly Property DecimalIndex() As Integer
        Get
            Return _decimalIndex
        End Get
    End Property
    Private _stringOfNumbers As New StringBuilder
    Public ReadOnly Property StringOfNumbers() As StringBuilder
        Get
            Return _stringOfNumbers
        End Get
    End Property
    Public Sub UpdateNumber(ByVal theDigitToAppend As Char)
        If IsNumeric(theDigitToAppend) Then
            Me._stringOfNumbers.Append(theDigitToAppend)
        ElseIf theDigitToAppend = DECIMAL_POINT_STRING_REPRESENTATION Then
            Me._hasDecimal = True
            Me._decimalIndex = Me._stringOfNumbers.Length
        ElseIf theDigitToAppend = NEGATIVE_NUMBER_STRING_REPRESENTATION Then
            Me._isNegative = Not Me._isNegative
        End If
    End Sub
    Public Shared Function ConvertDoubleToNumber(ByVal numberThatIsADouble As Double) As Number
        Dim numberResult As New Number
        For Each character As Char In numberThatIsADouble.ToString.ToCharArray
            numberResult.UpdateNumber(character)
        Next
        Return numberResult
    End Function
End Class
Public MustInherit Class Operation
    Protected _firstnumber As New Number
    Protected _secondnumber As New Number
    Public Property FirstNumber() As Number
        Get
            Return _firstnumber
        End Get
        Set(ByVal value As Number)
            _firstnumber = value
        End Set
    End Property
    Public Property SecondNumber() As Number
        Get
            Return _secondnumber
        End Get
        Set(ByVal value As Number)
            _secondnumber = value
        End Set
    End Property
End Class
Public Interface IOperatable
    Function PerformOperation(ByVal number1 As Double, ByVal number2 As Number) As Double
End Interface
Public Class Addition
    Inherits Operation
    Implements IOperatable
    Public Const ADDITION_STRING_REPRESENTATION As String = "+"
    Public Sub New()

    End Sub
    Public Function PerformOperation(ByVal number1 As Double, ByVal number2 As Number) As Double Implements IOperatable.PerformOperation
        Dim result As Double = 0
        result = number1 + number2.ActualNumber
        Return result
    End Function
End Class
Public Class Multiplication
    Inherits Operation
    Implements IOperatable
    Public Const MULTIPLICATION_STRING_REPRESENTATION As String = "*"
    Public Sub New()

    End Sub
    Public Function PerformOperation(ByVal number1 As Double, ByVal number2 As Number) As Double Implements IOperatable.PerformOperation
        Dim result As Double = 0
        result = number1 * number2.ActualNumber
        Return result
    End Function
End Class
Public Class Division
    Inherits Operation
    Implements IOperatable
    Public Const DIVISION_STRING_REPRESENTATION As String = "/"
    Public Const DIVIDE_BY_ZERO_ERROR_MESSAGE As String = "I took a lot of time to write this program. Please don't be a child and try to defile it by dividing by zero. Nobody thinks you are funny."
    Public Sub New()

    End Sub
    Public Function PerformOperation(ByVal number1 As Double, ByVal number2 As Number) As Double Implements IOperatable.PerformOperation
        If Not number2.ActualNumber = 0 Then
            Dim result As Double = 0
            result = number1 / number2.ActualNumber
            Return result
        Else
            Dim divideByZeroException As New Exception(DIVIDE_BY_ZERO_ERROR_MESSAGE)
            Throw divideByZeroException
        End If
    End Function
End Class
Public Class Parenthetical
    Public Const LEFT_PARENTHESIS_STRING_REPRESENTATION As String = "("
    Public Const RIGHT_PARENTHESIS_STRING_REPRESENTATION As String = ")"
    Private _allNumbers As New List(Of Number)
    Public Property AllNumbers() As List(Of Number)
        Get
            Return _allNumbers
        End Get
        Set(ByVal value As List(Of Number))
            _allNumbers = value
        End Set
    End Property
    Private _allOperators As New List(Of IOperatable)
    Public Property AllOperators() As List(Of IOperatable)
        Get
            Return _allOperators
        End Get
        Set(ByVal value As List(Of IOperatable))
            _allOperators = value
        End Set
    End Property
    Public Sub New()

    End Sub
    Public Function EvaluateParentheticalStatement() As Number
        Dim result As Double = 0
        Dim operationIndex As Integer = 0
        For Each numberOnWhichToPerformOperations As Number In AllNumbers
            result = AllOperators(operationIndex).PerformOperation(result, numberOnWhichToPerformOperations)
            operationIndex = operationIndex + 1
        Next

        Dim numberToReturn As New Number
        numberToReturn = Number.ConvertDoubleToNumber(result)
        Return numberToReturn
    End Function
End Class
End Class
Jason
quelle
2
Ich hätte wahrscheinlich auch 10.000 Charaktere treffen können, wenn es nicht so spät in der Nacht gewesen wäre :)
Jason
Wissen Sie, dass weniger Charaktere besser sind? Auf diese Weise denken sie nie, dass vb.net etwas Gutes ist.
Ikke
11
@ikke - sollte es so wenige Charaktere wie möglich sein? oh je ... jemand scheint den Punkt verpasst zu haben
Jason
3
ZERO_STRING_REPRESENTATION sieht aus wie etwas, das auf den täglichen wtf
erikkallen
1
+1 das brachte mich mehr zum Lachen als jede andere Antwort auf SO. jemals .
R .. GitHub STOP HELPING ICE
25

Haskell

Anzahl der Zeichen: 182

Kein Versuch der Klugheit, nur etwas Komprimierung: 4 Zeilen, 312 Bytes.

import Data.Char;import Text.ParserCombinators.Parsec
q=either(error.show)id.runParser t id"".filter(' '/=);t=do
s<-getState;a<-fmap read(many1$oneOf".-"<|>digit)<|>between(char '('>>setState id)(char ')'>>setState s)t
option(s a)$choice(zipWith(\c o->char c>>return(o$s a))"+-*/"[(+),(-),(*),(/)])>>=setState>>t

Und jetzt wirklich in den Golfgeist kommen, 3 Zeilen und 182 Bytes:

q=snd.(`e`id).filter(' '/=)
e s c|[(f,h)]<-readsPrec 0 s=g h(c f);e('(':s)c=g h(c f)where(')':h,f)=e s id
g('+':h)=e h.(+);g('-':h)=e h.(-);g('*':h)=e h.(*);g('/':h)=e h.(/);g h=(,)h

Explodiert:

-- Strip spaces from the input, evaluate with empty accumulator,
-- and output the second field of the result.
q :: String -> Double
q = snd . flip eval id . filter (not . isSpace)

-- eval takes a string and an accumulator, and returns
-- the final value and what’s left unused from the string.
eval :: (Fractional a, Read a) => String -> (a -> a) -> (String, a)

-- If the beginning of the string parses as a number, add it to the accumulator,
-- then try to read an operator and further.
eval str accum | [(num, rest)] <- readsPrec 0 str = oper rest (accum num)

-- If the string starts parentheses, evaluate the inside with a fresh
-- accumulator, and continue after the closing paren.
eval ('(':str) accum = oper rest (accum num) where (')':rest, num) = eval str id

-- oper takes a string and current value, and tries to read an operator
-- to apply to the value.  If there is none, it’s okay.
oper :: (Fractional a, Read a) => String -> a -> (String, a)

-- Handle operations by giving eval a pre-seeded accumulator.
oper ('+':str) num = eval str (num +)
oper ('-':str) num = eval str (num -)
oper ('*':str) num = eval str (num *)
oper ('/':str) num = eval str (num /)

-- If there’s no operation parsable, just return.
oper str num = (str, num)
kurzlebig
quelle
Ich vermute, dass es immer noch möglich ist, unter 225 zu kommen, aber das ist so weit, wie ich um 3 Uhr morgens kommen kann.
Ephemient
Das scheint eine recht elegante funktionale Lösung zu sein. (Eine, die ich ohne die Kommentare sicherlich nicht verstanden hätte, also danke dafür.) Außerdem sind Sie im Moment nur geringfügig vor Daves Python-Lösung, also scheinen Sie führend zu sein! Ich wäre gespannt, ob eine F # -Lösung dazu passen oder diese sogar übertreffen könnte.
Noldorin
Es ist für mich interessant, dass die Parsec-Lösung (Parser-Kombinatoren = verallgemeinerter regulärer Ausdruck + mehr), selbst wenn eine Minimierung versucht wurde, dem handgerollten Parsen nicht nahe kommt. Ich denke nicht, dass die Syntax von F # so
präzise sein
@ephemient: Ich habe versucht, sowohl Regex als auch das Parser-Modul in Python schnell zu verwenden. War fast sofort unter 300 Zeichen, sah aber keine Chance, wettbewerbsfähig zu werden. Das Problem ist der Import und die Funktionsaufrufe verschlingen zu viel. Das gilt für die meisten Sprachen (Ausnahme Perl). Übrigens müssen Sie sich nicht analysieren, um deutlich unter 300 Zeichen zu kommen, wie meine Lösung zeigt.
stephan
2
Ich denke, du bist damit fertig, deine Charakterzahl zu bestrafen. Das Problem erforderte eine String-> Double-Funktion, daher sollten Sie die Zeichen zählen, indem Sie "main = interaktive $ show" ersetzen. mit "q =" für 17 weitere Zeichen, setzen Sie Ihre Zählung auf 209.
Daniel Martin
23

Python

Anzahl der Zeichen: 237

Vollständig verschleierte Funktion:

from operator import*
def e(s,l=[]):
 if s:l+=list(s.replace(' ','')+')')
 a=0;o=add;d=dict(zip(')*+-/',(0,mul,o,sub,div)));p=l.pop
 while o:
  c=p(0)
  if c=='(':c=e(0)
  while l[0]not in d:c+=p(0)
  a=o(a,float(c));o=d[p(0)]
 return a

Klare / halbverschleierte Funktion:

import operator

def calc(source, stack=[]):
    if source:
        stack += list(source.replace(' ', '') + ')')

    answer = 0

    ops = {
        ')': 0,
        '*': operator.mul,
        '+': operator.add,
        '-': operator.sub,
        '/': operator.div,
    }

    op = operator.add
    while op:
        cur = stack.pop(0)

        if cur == '(':
            cur = calc(0)

        while stack[0] not in ops:
            cur += stack.pop(0)

        answer = op(answer, float(cur))
        op = ops[stack.pop(0)]

    return answer
Dave
quelle
Hinweis: Die aktuelle Version verarbeitet keine Eingaben wie - (1.0), obwohl negative Literale korrekt verarbeitet werden. Aus der Spezifikation war nicht ersichtlich, ob dies erforderlich ist.
Dave
Man kann l kostenlos nicht global machen, indem man es in die Parameterliste von e steckt. Trotzdem ist es nicht threadsicher.
Dave
Sehr gerissen. Das Dolmetschen hat sich gelohnt. :)
Nick Johnson
@ Dave: Meins fällt -(1.0)auch aus, also keine Sorge! Ich werde die Frage klären. Wie auch immer, es scheint eine sehr clevere Lösung zu sein - ich versuche immer noch herauszufinden, wie es funktioniert (ich kenne Python nicht genau). Wenn Sie eine kurze Erklärung hinzufügen könnten, wäre dies sehr dankbar.
Noldorin
20

Fortran 77 (Gfortran-Dialekt, jetzt mit G77-Unterstützung)

Anzahl der Zeichen: 2059

Verschleierte Version:

      function e(c)
      character*99 c
      character b
      real f(24)                
      integer i(24)             
      nf=0                      
      ni=0                      
 20   nf=kf(0.0,nf,f)
      ni=ki(43,ni,i)         
 30   if (isp(c).eq.1) goto 20
      h=fr(c)
 31   g=fp(nf,f)
      j=ip(ni,i)
      select case(j)
      case (40) 
         goto 20
      case (42)                 
         d=g*h
      case (43)                 
         d=g+h
      case (45)                 
         d=g-h
      case (47)                 
         d=g/h
      end select
 50   nf=kf(d,nf,f)
 60   j=nop(c)
      goto (20, 70, 75, 75, 60, 75, 60, 75) (j-39)
 65   e=fp(nf,f)
      return
 70   h=fp(nf,f)              
      goto 31
 75   ni=ki(j,ni,i)
      goto 30
      end
      function kf(v,n,f)
      real f(24)
      kf=n+1
      f(n+1)=v
      return
      end
      function ki(j,n,i)
      integer i(24)
      ki=n+1
      i(n+1)=j
      return
      end
      function fp(n,f)
      real f(24)
      fp=f(n)
      n=n-1
      return
      end
      function ip(n,i)
      integer i(24)
      ip=i(n)
      n=n-1
      return
      end
      function nop(s)
      character*99 s
      l=1
      do while(s(l:l).eq." ".and.l.lt.99)
         l=l+1
      enddo
      nop=ichar(s(l:l))
      s(l:l)=" "
      return
      end
      function isp(s)
      character*99 s
      isp=0
      l=1
      do while(s(l:l).eq." ".and.l.lt.99)
         l=l+1
      enddo
      isp=41-ichar(s(l:l))
      if (isp.eq.1) s(l:l)=" "
      return
      end
      function fr(s)
      character*99 s
      m=1                      
      n=1                      
      i=1
      do while(i.le.99)
         j=ichar(s(i:i))
         if (j.eq.32) goto 90   
         if (j.ge.48.and.j.lt.58) goto 89
         if (j.eq.43.or.j.eq.45) goto (89,80) m
         if (j.eq.46) goto (83,80) n
 80      exit
 83      n=2
 89      m=2
 90      i=i+1
      enddo
      read(s(1:i-1),*) fr
      do 91 j=1,i-1
         s(j:j)=" "
 91   continue
      return 
      end

Klare Version: (3340 Zeichen mit Gerüst)

      program infixeval
      character*99 c
      do while (.true.)
         do 10 i=1,99
            c(i:i)=" "
 10      continue
         read(*,"(A99)") c
         f=e(c)
         write(*,*)f
      enddo
      end

      function e(c)
      character*99 c
      character b
      real f(24)                ! value stack
      integer i(24)             ! operator stack
      nf=0                      ! number of items on the value stack
      ni=0                      ! number of items on the operator stack
 20   nf=pushf(0.0,nf,f)
      ni=pushi(43,ni,i)         ! ichar(+) = 43
D     write (*,*) "'",c,"'"
 30   if (isp(c).eq.1) goto 20
      h=fr(c)
D     write (*,*) "'",c,"'"
 31   g=fpop(nf,f)
      j=ipop(ni,i)
D     write(*,*) "Opperate ",g," ",char(j)," ",h
      select case(j)
      case (40) 
         goto 20
      case (42)                 ! "*" 
         d=g*h
      case (43)                 ! "+"
         d=g+h
      case (45)                 ! "-"
         d=g-h
      case (47)                 ! "*"
         d=g/h
      end select
 50   nf=pushf(d,nf,f)
 60   j=nop(c)
D     write(*,*) "Got op: ", char(j)
      goto (20, 70, 75, 75, 60, 75, 60, 75) (j-39)
 65   e=fpop(nf,f)
      return
 70   h=fpop(nf,f)              ! Encountered a "("
      goto 31
 75   ni=pushi(j,ni,i)
      goto 30
      end

c     push onto a real stack
c     OB as kf
      function pushf(v,n,f)
      real f(24)
      pushf=n+1
      f(n+1)=v
D     write(*,*) "Push ", v
      return
      end

c     push onto a integer stack
c     OB as ki
      function pushi(j,n,i)
      integer i(24)
      pushi=n+1
      i(n+1)=j
D     write(*,*) "Push ", char(j)
      return
      end

c     pop from real stack
c     OB as fp
      function fpop(n,f)
      real f(24)
      fpop=f(n)
      n=n-1
D      write (*,*) "Pop ", fpop
      return
      end

c     pop from integer stack
c     OB as ip
      function ipop(n,i)
      integer i(24)
      ipop=i(n)
      n=n-1
D      write (*,*) "Pop ", char(ipop)
      return
      end

c     Next OPerator: returns the next nonws character, and removes it
c     from the string
      function nop(s)
      character*99 s
      l=1
      do while(s(l:l).eq." ".and.l.lt.99)
         l=l+1
      enddo
      nop=ichar(s(l:l))
      s(l:l)=" "
      return
      end

c     IS an open Paren: return 1 if the next non-ws character is "("
c     (also overwrite it with a space. Otherwise return not 1
      function isp(s)
      character*99 s
      isp=0
      l=1
      do while(s(l:l).eq." ".and.l.lt.99)
         l=l+1
      enddo
      isp=41-ichar(s(l:l))
      if (isp.eq.1) s(l:l)=" "
      return
      end

c     Float Read: return the next real number in the string and removes the
c     character
      function fr(s)
      character*99 s
      m=1                      ! No sign (Minus or plus) so far
      n=1                      ! No decimal so far
      i=1
      do while(i.le.99)
         j=ichar(s(i:i))
         if (j.eq.32) goto 90   ! skip spaces
         if (j.ge.48.and.j.lt.58) goto 89
         if (j.eq.43.or.j.eq.45) goto (89,80) m
         if (j.eq.46) goto (83,80) n
c     not part of a number
 80      exit
 83      n=2
 89      m=2
 90      i=i+1
      enddo
      read(s(1:i-1),*) fr
      do 91 j=1,i-1
         s(j:j)=" "
 91   continue
      return 
      end

Anmerkungen Diese bearbeitete Version ist eher böse als mein erster Versuch. Gleicher Algorithmus, aber jetzt inline mit einem schrecklichen Gewirr von gotos. Ich habe die Co-Routinen aufgegeben, verwende jetzt aber einige Varianten von berechneten Zweigen. Alle Fehlerprüfungen und Berichte wurden entfernt, aber diese Version wird stillschweigend von einigen Klassen unerwarteter Zeichen in der Eingabe wiederhergestellt. Diese Version wird auch mit g77 kompiliert.

Die primären Grenzen sind immer noch die starre Formatierung von fortran, lange und allgegenwärtige Schlüsselwörter und einfache Grundelemente.

dmckee
quelle
14
Guter Gott, Mann! Sie müssen sich heute gelangweilt haben. ;)
Gnovice
2
Hehe, ich glaube nicht, dass ich jemals eine Fortran-Lösung erwartet habe! Ich denke, wir können daraus schließen, dass die Sprache nicht besonders gut für Code-Golf geeignet ist. Trotzdem für die bloße Anstrengung und die Verwendung einer antiquierten Sprache gestimmt. :)
Noldorin
Ich finde diese Art von fummeligem Byte-Diddling wortreich und umständlich in fortran, aber nicht wirklich schwer. Das Schreiben von unstrukturiertem Code und das Verwenden dieser berechneten Zweige fühlt sich dagegen etwas pervers an.
dmckee --- Ex-Moderator Kätzchen
Gut gemacht, aber wie bekommt eine Fortran-Version mit mehr als 2000 Zeichen mehr Stimmen als meine kurze kleine Ruby1.9-Version? lol
Daniel Huckstep
@ Darkhelmet: Ich habe keine Ahnung. Ich tat es auf einer Lerche und erwartete ein oder zwei Stimmen für Anstrengung und Perversität. Ich bin obszön stolz auf diesen Gräuel, aber das ist lächerlich ...
dmckee --- Ex-Moderator Kätzchen
17

C99

Anzahl der Zeichen: 239 ( 209 siehe unten )

komprimierte Funktion:

#define S while(*e==32)++e
#define F float
F strtof();char*e;F v();F g(){S;return*e++-40?strtof(e-1,&e):v();}F v(){F b,a=g();for(;;){S;F o=*e++;if(!o|o==41)return a;b=g();a=o==43?a+b:o==45?a-b:o==42?a*b:a/b;}}F f(char*x){e=x;return v();}

dekomprimierte Funktion:

float strtof();

char* e;
float v();

float g() {
    while (*e == ' ') ++e;
    return *e++ != '(' ? strtof(e-1, &e) : v();
}

float v() {
    float b, a = g();
    for (;;) {
        while (*e == ' ') ++e;
        float op = *e++;
        if (op == 0 || op == ')') return a;
        b = g();
        a = op == '+' ? a + b : op == '-' ? a - b : op == '*' ? a * b : a / b;
    }
}

float eval(char* x) {
    e = x;
    return v();
}

Funktion ist nicht wiedereintrittsfähig.

EDIT von Chris Lutz : Ich hasse es, den Code eines anderen Mannes mit Füßen zu treten, aber hier ist eine 209- Zeichen-Version:

#define S for(;*e==32;e++)
#define X (*e++-40?strtof(e-1,&e):v())
float strtof();char*e;float v(){float o,a=X;for(;;){S;o=*e++;if(!o|o==41)return a;S;a=o-43?o-45?o-42?a/X:a*X:a-X:a+X;}}
#define f(x) (e=x,v())

Lesbar (gut, nicht wirklich gut lesbar, aber dekomprimiert):

float strtof();
char *e;
float v() {
    float o, a = *e++ != '(' ? strtof(e - 1, &e) : v();
    for(;;) {
        for(; *e == ' '; e++);
        o = *e++;
        if(o == 0 || o==')') return a;
        for(; *e == ' '; e++);
        // I have no idea how to properly indent nested conditionals
        // and this is far too long to fit on one line.
        a = o != '+' ?
          o != '-' ?
            o != '*' ?
              a / (*e++ != '(' ? strtof(e - 1, &e) : v()) :
              a * (*e++ != '(' ? strtof(e - 1, &e) : v()) :
            a - (*e++ != '(' ? strtof(e - 1, &e) : v()) :
          a + (*e++ != '(' ? strtof(e - 1, &e) : v());
      }
}
#define f(x) (e = x, v())

Ja, f()ist ein Makro, keine Funktion, aber es funktioniert. In der lesbaren Version wurde ein Teil der Logik neu geschrieben, aber nicht neu angeordnet (wie o != '+'anstelle von o - '+'), sondern es handelt sich ansonsten nur um eine eingerückte (und vorverarbeitete) Version der anderen. Ich versuche immer wieder, das if(!o|o==41)return a;Teil in der for()Schleife zu vereinfachen , aber es macht es nie kürzer. Ich glaube immer noch, dass es möglich ist, aber ich bin mit dem Golfen fertig. Wenn ich an dieser Frage weiter arbeite, wird sie in der Sprache sein, die nicht benannt werden darf .

Ferruccio
quelle
Gute Lösung und Bonuspunkte für die Verwendung von "reinem" C. Schlägt meine ebenfalls um 3 Zeichen! Wiedereintritt war nicht in den Regeln, also ist das in Ordnung. (Es ist jedoch ein Plus.)
Noldorin
Nett! Sie können ein paar weitere Zeichen durch Verwendung von ASCII-Codes rasieren, z. B. '0' durch 48 ersetzen usw. Und natürlich können Sie eine Menge speichern, indem Sie atof () anstelle Ihres selbst entwickelten Float-Parsers verwenden, aber Sie sind es absichtlich keine Bibliotheksfunktionen verwenden, was keine strikte Anforderung des Problems ist.
Adam Rosenfield
Ich habe überlegt, atof () zu verwenden, aber es sagt Ihnen nicht, wo die Float-Zeichenfolge endet, sodass Sie sie trotzdem analysieren müssten.
Ferruccio
Danke für den Tipp, Adam. Mit diesem und ein paar anderen (hässlichen) Tricks habe ich es etwas weiter geschrumpft.
Ferruccio
Autsch, ich habe nicht mit negativen Zahlen gerechnet. Code auf 400 Zeichen aufgeblasen.
Ferruccio
13

Common Lisp

(SBCL)
Anzahl der Zeichen: 251

(defun g(e)(if(numberp e)e(let((m (g (pop e)))(o(loop for x in e by #'cddr collect x))(n(loop for x in (cdr e)by #'cddr collect (g x))))(mapcar(lambda(x y)(setf m(apply x(list m y))))o n)m)))(defun w(e)(g(read-from-string(concatenate'string"("e")"))))

Richtige Version (387 Zeichen):

(defun wrapper (exp) (golf-eval (read-from-string (concatenate 'string "(" exp ")"))))

(defun golf-eval (exp)
 (if (numberp exp)
     exp
   (let ((mem (golf-eval (pop exp)))
     (op-list (loop for x in exp by #'cddr collect x))
     (num-list (loop for x in (cdr exp) by #'cddr collect (golf-eval x))))
    (mapcar (lambda (x y) (setf mem (apply x (list mem y)))) op-list num-list)
    mem)))

Die Eingabe ist eine Form w(), die ein Zeichenfolgenargument akzeptiert. Es verwendet den Trick, dass sich Zahlen / Operanden und Operatoren im Muster NONON befinden ... und wertet alle Operanden rekursiv aus, wodurch das Verschachteln sehr billig wird. ;)

Benutzer110763
quelle
Clevere Lösung. Trotzdem bin ich mir nicht ganz sicher, ob es vollständig gültig ist, da die Spezifikation für die Funktion war, ein Zeichenfolgenobjekt aufzunehmen.
Noldorin
Das tut mir leid. Fest!
Kein Problem. Ich wusste nicht, dass die Konvertierung so einfach war. Trotzdem eine gute Lösung!
Noldorin
Beeindruckend. Das ist wunderschön. :)
Emil H
11

JavaScript (nicht IE-kompatibel)

Anzahl der Zeichen: 268/260

Vollständig verschleierte Funktion:

function e(x){x=x.replace(/ /g,'')+')'
function P(n){return x[0]=='('?(x=x.substr(1),E()):(n=/^[-+]?[\d.]+/(x)[0],x=x.substr(n.length),+n)}function E(a,o,b){a=P()
for(;;){o=x[0]
x=x.substr(1)
if(o==')')return a
b=P()
a=o=='+'?a+b:o=='-'?a-b:o=='*'?a*b:a/b}}return E()}

In JavaScript 1.8 (Firefox 3+) können Sie einige Zeichen mithilfe von Ausdrucksschließungen speichern:

e=function(x,P,E)(x=x.replace(/ /g,'')+')',P=function(n)(x[0]=='('?(x=x.substr(1),E()):(n=/^[-+]?[\d.]+/(x)[0],x=x.substr(n.length),+n)),E=function(a,o,b){a=P()
for(;;){o=x[0]
x=x.substr(1)
if(o==')')return a
b=P()
a=o=='+'?a+b:o=='-'?a-b:o=='*'?a*b:a/b}},E())

Klare / halbverschleierte Funktion:

function evaluate(x) {
    x = x.replace(/ /g, "") + ")";
    function primary() {
        if (x[0] == '(') {
            x = x.substr(1);
            return expression();
        }

        var n = /^[-+]?\d*\.?\d*/.exec(x)[0];
        x = x.substr(n.length);
        return +n;
    }

    function expression() {
        var a = primary();
        for (;;) {
            var operator = x[0];
            x = x.substr(1);

            if (operator == ')') {
                return a;
            }

            var b = primary();
            a = (operator == '+') ? a + b :
                (operator == '-') ? a - b :
                (operator == '*') ? a * b :
                                    a / b;
        }
    }

    return expression();
}

Keine der beiden Versionen funktioniert im IE, da für die Zeichenfolge Subskriptionen im Array-Stil verwendet werden. Wenn Sie beide Vorkommen von x[0]durch ersetzen x.charAt(0), sollte das erste überall funktionieren.

Ich habe seit der ersten Version einige weitere Zeichen ausgeschnitten, indem ich Variablen in Funktionsparameter umgewandelt und eine andere if-Anweisung durch den bedingten Operator ersetzt habe.

Matthew Crumley
quelle
Das ist sehr gut. Ich habe darauf gewartet, dass hier jemand Regex verwendet. :) Es scheint, als hätten dynamische Sprachen definitiv einen Vorteil für dieses Problem.
Noldorin
8

C # mit Regex Love

Anzahl der Zeichen: 384

Vollständig verschleiert:

float E(string i){i=i.Replace(" ","");Regex b=new Regex(@"\((?>[^()]+|\((?<D>)|\)(?<-D>))*(?(D)(?!))\)");i=b.Replace(i,m=>Eval(m.Value.Substring(1,m.Length-2)).ToString());float r=0;foreach(Match m in Regex.Matches(i,@"(?<=^|\D)-?[\d.]+")){float f=float.Parse(m.Value);if(m.Index==0)r=f;else{char o=i[m.Index-1];if(o=='+')r+=f;if(o=='-')r-=f;if(o=='*')r*=f;if(o=='/')r/=f;}}return r;}

Nicht verschleiert:

private static float Eval(string input)
{
    input = input.Replace(" ", "");
    Regex balancedMatcher = new Regex(@"\(
                                            (?>
                                                [^()]+
                                            |
                                                \( (?<Depth>)
                                            |
                                                \) (?<-Depth>)
                                            )*
                                            (?(Depth)(?!))
                                        \)", RegexOptions.IgnorePatternWhitespace);
    input = balancedMatcher.Replace(input, m => Eval(m.Value.Substring(1, m.Length - 2)).ToString());

    float result = 0;

    foreach (Match m in Regex.Matches(input, @"(?<=^|\D)-?[\d.]+"))
    {
        float floatVal = float.Parse(m.Value);
        if (m.Index == 0)
        {
            result = floatVal;
        }
        else
        {
            char op = input[m.Index - 1];
            if (op == '+') result += floatVal;
            if (op == '-') result -= floatVal;
            if (op == '*') result *= floatVal;
            if (op == '/') result /= floatVal;
        }
    }

    return result;
}

Nutzt die Regex- Ausgleichsgruppenfunktion von .NET .

Jeff Moser
quelle
Danke für diese Lösung. :) Ich war mir nicht sicher, ob ich eine C # -Lösung mit Regex sehen würde, aber hier haben wir sie. Nun ist es fraglich, ob Sie "using System.Text.RegularExpressions" einfügen sollten. in Ihrer Zeichenzahl, aber es ist trotzdem eine gute Lösung.
Noldorin
Das war nicht Teil der Regeln :). Wenn Sie hinzufügen "using R = System.Text.RegularExpressions.Regex;" und ersetze meinen "Regex" durch R, er geht an 417.
Jeff Moser
@ Jeff: Nun, technisch gesehen wird es ohne die using-Anweisung nicht kompiliert, daher sollte es standardmäßig enthalten sein. Kleinigkeit jedoch, da unsere C # -Lösungen alle deutlich hinter dem Marktführer liegen.
Noldorin
8

PHP

Anzahl der Zeichen: 284

verschleiert:

function f($m){return c($m[1]);}function g($n,$m){$o=$m[0];$m[0]=' ';return$o=='+'?$n+$m:($o=='-'?$n-$m:($o=='*'?$n*$m:$n/$m));}function c($s){while($s!=($t=preg_replace_callback('/\(([^()]*)\)/',f,$s)))$s=$t;preg_match_all('![-+/*].*?[\d.]+!',"+$s",$m);return array_reduce($m[0],g);}

lesbar:

function callback1($m) {return c($m[1]);}
function callback2($n,$m) {
    $o=$m[0];
    $m[0]=' ';
    return $o=='+' ? $n+$m : ($o=='-' ? $n-$m : ($o=='*' ? $n*$m : $n/$m));
}
function c($s){ 
    while ($s != ($t = preg_replace_callback('/\(([^()]*)\)/','callback1',$s))) $s=$t;
    preg_match_all('![-+/*].*?[\d.]+!', "+$s", $m);
    return array_reduce($m[0], 'callback2');
}


$str = '  2.45/8.5  *  -9.27   +    (   5   *  0.0023  ) ';
var_dump(c($str));
# float(-2.66044117647)

Sollte mit jeder gültigen Eingabe funktionieren (einschließlich negativer Zahlen und beliebiger Leerzeichen)

Soulmerge
quelle
preg_replace()mit dem eModifikator würden Sie einige weitere Bytes sparen.
Alix Axel
8

SQL (SQL Server 2008)

Anzahl der Zeichen: 4202

Vollständig verschleierte Funktion:

WITH Input(id,str)AS(SELECT 1,'1 + 3 / -8'UNION ALL SELECT 2,'2*3*4*5+99'UNION ALL SELECT 3,'4 * (9 - 4)/ (2 * 6 - 2)+ 8'UNION ALL SELECT 4,'1 + ((123 * 3 - 69)/ 100)'UNION ALL SELECT 5,'2.45/8.5*9.27+(5*0.0023)'),Separators(i,ch,str_src,priority)AS(SELECT 1,'-',1,1UNION ALL SELECT 2,'+',1,1UNION ALL SELECT 3,'*',1,1UNION ALL SELECT 4,'/',1,1UNION ALL SELECT 5,'(',0,0UNION ALL SELECT 6,')',0,0),SeparatorsStrSrc(str,i)AS(SELECT CAST('['AS varchar(max)),0UNION ALL SELECT str+ch,SSS.i+1FROM SeparatorsStrSrc SSS INNER JOIN Separators S ON SSS.i=S.i-1WHERE str_src<>0),SeparatorsStr(str)AS(SELECT str+']'FROM SeparatorsStrSrc WHERE i=(SELECT COUNT(*)FROM Separators WHERE str_src<>0)),ExprElementsSrc(id,i,tmp,ele,pre_ch,input_str)AS(SELECT id,1,CAST(LEFT(str,1)AS varchar(max)),CAST(''AS varchar(max)),CAST(' 'AS char(1)),SUBSTRING(str,2,LEN(str))FROM Input UNION ALL SELECT id,CASE ele WHEN''THEN i ELSE i+1 END,CAST(CASE WHEN LEFT(input_str,1)=' 'THEN''WHEN tmp='-'THEN CASE WHEN pre_ch LIKE(SELECT str FROM SeparatorsStr)THEN tmp+LEFT(input_str,1)ELSE LEFT(input_str,1)END WHEN LEFT(input_str,1)IN(SELECT ch FROM Separators)OR tmp IN(SELECT ch FROM Separators)THEN LEFT(input_str,1)ELSE tmp+LEFT(input_str,1)END AS varchar(max)),CAST(CASE WHEN LEFT(input_str,1)=' 'THEN tmp WHEN LEFT(input_str,1)='-'THEN CASE WHEN tmp IN(SELECT ch FROM Separators)THEN tmp ELSE''END WHEN LEFT(input_str,1)IN(SELECT ch FROM Separators)OR tmp IN(SELECT ch FROM Separators)THEN CASE WHEN tmp='-'AND pre_ch LIKE(SELECT str FROM SeparatorsStr)THEN''ELSE tmp END ELSE''END AS varchar(max)),CAST(LEFT(ele,1)AS char(1)),SUBSTRING(input_str,2,LEN(input_str))FROM ExprElementsSrc WHERE input_str<>''OR tmp<>''),ExprElements(id,i,ele)AS(SELECT id,i,ele FROM ExprElementsSrc WHERE ele<>''),Scanner(id,i,val)AS(SELECT id,i,CAST(ele AS varchar(max))FROM ExprElements WHERE ele<>''UNION ALL SELECT id,MAX(i)+1,NULL FROM ExprElements GROUP BY id),Operator(op,priority)AS(SELECT ch,priority FROM Separators WHERE priority<>0),Calc(id,c,i,pop_count,s0,s1,s2,stack,status)AS(SELECT Scanner.id,1,1,0,CAST(scanner.val AS varchar(max)),CAST(NULL AS varchar(max)),CAST(NULL AS varchar(max)),CAST(''AS varchar(max)),CAST('init'AS varchar(max))FROM Scanner WHERE Scanner.i=1UNION ALL SELECT Calc.id,Calc.c+1,Calc.i,3,NULL,NULL,NULL,CASE Calc.s1 WHEN'+'THEN CAST(CAST(Calc.s2 AS real)+CAST(Calc.s0 AS real)AS varchar(max))WHEN'-'THEN CAST(CAST(Calc.s2 AS real)-CAST(Calc.s0 AS real)AS varchar(max))WHEN'*'THEN CAST(CAST(Calc.s2 AS real)*CAST(Calc.s0 AS real)AS varchar(max))WHEN'/'THEN CAST(CAST(Calc.s2 AS real)/CAST(Calc.s0 AS real)AS varchar(max))ELSE NULL END+' '+stack,CAST('calc '+Calc.s1 AS varchar(max))FROM Calc INNER JOIN Scanner NextVal ON Calc.id=NextVal.id AND Calc.i+1=NextVal.i WHERE Calc.pop_count=0AND ISNUMERIC(Calc.s2)=1AND Calc.s1 IN(SELECT op FROM Operator)AND ISNUMERIC(Calc.s0)=1AND(SELECT priority FROM Operator WHERE op=Calc.s1)>=COALESCE((SELECT priority FROM Operator WHERE op=NextVal.val),0)UNION ALL SELECT Calc.id,Calc.c+1,Calc.i,3,NULL,NULL,NULL,s1+' '+stack,CAST('paren'AS varchar(max))FROM Calc WHERE pop_count=0AND s2='('AND ISNUMERIC(s1)=1AND s0=')'UNION ALL SELECT Calc.id,Calc.c+1,Calc.i,Calc.pop_count-1,s1,s2,CASE WHEN LEN(stack)>0THEN SUBSTRING(stack,1,CHARINDEX(' ',stack)-1)ELSE NULL END,CASE WHEN LEN(stack)>0THEN SUBSTRING(stack,CHARINDEX(' ',stack)+1,LEN(stack))ELSE''END,CAST('pop'AS varchar(max))FROM Calc WHERE Calc.pop_count>0UNION ALL SELECT Calc.id,Calc.c+1,Calc.i+1,Calc.pop_count,CAST(NextVal.val AS varchar(max)),s0,s1,coalesce(s2,'')+' '+stack,cast('read'as varchar(max))FROM Calc INNER JOIN Scanner NextVal ON Calc.id=NextVal.id AND Calc.i+1=NextVal.i WHERE NextVal.val IS NOT NULL AND Calc.pop_count=0AND((Calc.s0 IS NULL OR calc.s1 IS NULL OR calc.s2 IS NULL)OR NOT(ISNUMERIC(Calc.s2)=1AND Calc.s1 IN(SELECT op FROM Operator)AND ISNUMERIC(calc.s0)=1AND (SELECT priority FROM Operator WHERE op=Calc.s1)>=COALESCE((SELECT priority FROM Operator WHERE op=NextVal.val),0))AND NOT(s2='('AND ISNUMERIC(s1)=1AND s0=')')))SELECT Calc.id,Input.str,Calc.s0 AS result FROM Calc INNER JOIN Input ON Calc.id=Input.id WHERE Calc.c=(SELECT MAX(c)FROM Calc calc2 WHERE Calc.id=Calc2.id)ORDER BY id

Klare / halbverschleierte Funktion:

WITH
  Input(id, str) AS (    
    SELECT 1, '1 + 3 / -8'
    UNION ALL SELECT 2, '2*3*4*5+99'
    UNION ALL SELECT 3, '4 * (9 - 4) / (2 * 6 - 2) + 8'
    UNION ALL SELECT 4, '1 + ((123 * 3 - 69) / 100)'
    UNION ALL SELECT 5, '2.45/8.5*9.27+(5*0.0023)'
  )
, Separators(i, ch, str_src, priority) AS (
    SELECT 1, '-', 1, 1
    UNION ALL SELECT 2, '+', 1, 1
    UNION ALL SELECT 3, '*', 1, 1
    UNION ALL SELECT 4, '/', 1, 1
    UNION ALL SELECT 5, '(', 0, 0
    UNION ALL SELECT 6, ')', 0, 0
  )
, SeparatorsStrSrc(str, i) AS (
    SELECT CAST('[' AS varchar(max)), 0
    UNION ALL
    SELECT
        str + ch
      , SSS.i + 1
    FROM
        SeparatorsStrSrc SSS
          INNER JOIN Separators S ON SSS.i = S.i - 1
    WHERE
        str_src <> 0
  )
, SeparatorsStr(str) AS (
    SELECT str + ']' FROM SeparatorsStrSrc
    WHERE i = (SELECT COUNT(*) FROM Separators WHERE str_src <> 0)
  )
, ExprElementsSrc(id, i, tmp, ele, pre_ch, input_str) AS (
    SELECT
        id
      , 1
      , CAST(LEFT(str, 1) AS varchar(max))
      , CAST('' AS varchar(max))
      , CAST(' ' AS char(1))
      , SUBSTRING(str, 2, LEN(str))
    FROM
        Input
    UNION ALL
    SELECT
        id
      , CASE ele
        WHEN '' THEN i
                ELSE i + 1
        END
      , CAST(
          CASE
          WHEN LEFT(input_str, 1) = ' '
            THEN ''
          WHEN tmp = '-'
            THEN CASE
                 WHEN pre_ch LIKE (SELECT str FROM SeparatorsStr)
                   THEN tmp + LEFT(input_str, 1)
                   ELSE LEFT(input_str, 1)
                 END
          WHEN LEFT(input_str, 1) IN (SELECT ch FROM Separators)
               OR
               tmp IN (SELECT ch FROM Separators)
            THEN LEFT(input_str, 1)
            ELSE tmp + LEFT(input_str, 1)
          END
        AS varchar(max))
      , CAST(
          CASE
          WHEN LEFT(input_str, 1) = ' '
            THEN tmp
          WHEN LEFT(input_str, 1) = '-'
            THEN CASE
                 WHEN tmp IN (SELECT ch FROM Separators)
                   THEN tmp
                   ELSE ''
                 END
          WHEN LEFT(input_str, 1) IN (SELECT ch FROM Separators)
               OR
               tmp IN (SELECT ch FROM Separators)
            THEN CASE
                 WHEN tmp = '-' AND pre_ch LIKE (SELECT str FROM SeparatorsStr)
                   THEN ''
                   ELSE tmp
                 END
            ELSE ''
          END
        AS varchar(max))
      , CAST(LEFT(ele, 1) AS char(1))
      , SUBSTRING(input_str, 2, LEN(input_str))
    FROM
        ExprElementsSrc
    WHERE
        input_str <> ''
        OR
        tmp <> ''
  )
, ExprElements(id, i, ele) AS (
    SELECT
        id
      , i
      , ele
    FROM
        ExprElementsSrc
    WHERE
        ele <> ''
  )
, Scanner(id, i, val) AS (
    SELECT
        id
      , i
      , CAST(ele AS varchar(max))
    FROM
        ExprElements
    WHERE
        ele <> ''
    UNION ALL
    SELECT
        id
      , MAX(i) + 1
      , NULL
    FROM
        ExprElements
    GROUP BY
        id
  )
, Operator(op, priority) AS (
    SELECT
        ch
      , priority 
    FROM
        Separators
    WHERE
        priority <> 0
  )
, Calc(id, c, i, pop_count, s0, s1, s2, stack, status) AS (
    SELECT
        Scanner.id
      , 1
      , 1
      , 0
      , CAST(scanner.val AS varchar(max))
      , CAST(NULL AS varchar(max))
      , CAST(NULL AS varchar(max))
      , CAST('' AS varchar(max))
      , CAST('init' AS varchar(max))
    FROM
        Scanner
    WHERE
        Scanner.i = 1
    UNION ALL
    SELECT
        Calc.id
      , Calc.c + 1
      , Calc.i
      , 3
      , NULL
      , NULL
      , NULL
      , CASE Calc.s1
        WHEN '+' THEN CAST(CAST(Calc.s2 AS real) + CAST(Calc.s0 AS real) AS varchar(max))
        WHEN '-' THEN CAST(CAST(Calc.s2 AS real) - CAST(Calc.s0 AS real) AS varchar(max))
        WHEN '*' THEN CAST(CAST(Calc.s2 AS real) * CAST(Calc.s0 AS real) AS varchar(max))
        WHEN '/' THEN CAST(CAST(Calc.s2 AS real) / CAST(Calc.s0 AS real) AS varchar(max))
                 ELSE NULL
        END
          + ' '
          + stack
      , CAST('calc ' + Calc.s1 AS varchar(max))
    FROM
        Calc
          INNER JOIN Scanner NextVal ON Calc.id = NextVal.id
                                          AND Calc.i + 1 = NextVal.i
    WHERE
        Calc.pop_count = 0
          AND ISNUMERIC(Calc.s2) = 1
          AND Calc.s1 IN (SELECT op FROM Operator)
          AND ISNUMERIC(Calc.s0) = 1
          AND (SELECT priority FROM Operator WHERE op = Calc.s1)
            >= COALESCE((SELECT priority FROM Operator WHERE op = NextVal.val), 0)
    UNION ALL
    SELECT
        Calc.id
      , Calc.c + 1
      , Calc.i
      , 3
      , NULL
      , NULL
      , NULL
      , s1 + ' ' + stack
      , CAST('paren' AS varchar(max))
    FROM
        Calc
    WHERE
        pop_count = 0
          AND s2 = '('
          AND ISNUMERIC(s1) = 1
          AND s0 = ')'
    UNION ALL
    SELECT
        Calc.id
      , Calc.c + 1
      , Calc.i
      , Calc.pop_count - 1
      , s1
      , s2
      , CASE
        WHEN LEN(stack) > 0
          THEN SUBSTRING(stack, 1, CHARINDEX(' ', stack) - 1)
          ELSE NULL
        END
      , CASE
        WHEN LEN(stack) > 0
          THEN SUBSTRING(stack, CHARINDEX(' ', stack) + 1, LEN(stack))
          ELSE ''
        END
      , CAST('pop' AS varchar(max))
    FROM
        Calc
    WHERE
        Calc.pop_count > 0
    UNION ALL
    SELECT
        Calc.id
      , Calc.c + 1
      , Calc.i + 1
      , Calc.pop_count
      , CAST(NextVal.val AS varchar(max))
      , s0
      , s1
      , coalesce(s2, '') + ' ' + stack
      , cast('read' as varchar(max))
    FROM
        Calc
          INNER JOIN Scanner NextVal ON Calc.id = NextVal.id
                                          AND Calc.i + 1 = NextVal.i
    WHERE
        NextVal.val IS NOT NULL
          AND Calc.pop_count = 0
          AND (
            (Calc.s0 IS NULL or calc.s1 is null or calc.s2 is null)
            OR
            NOT(
              ISNUMERIC(Calc.s2) = 1
                AND Calc.s1 IN (SELECT op FROM Operator)
                AND ISNUMERIC(calc.s0) = 1
                AND (SELECT priority FROM Operator WHERE op = Calc.s1)
                  >= COALESCE((SELECT priority FROM Operator WHERE op = NextVal.val), 0)
            )
              AND NOT(s2 = '(' AND ISNUMERIC(s1) = 1 AND s0 = ')')
          )
  )
SELECT
    Calc.id
  , Input.str
  , Calc.s0 AS result
FROM
    Calc
      INNER JOIN Input ON Calc.id = Input.id
WHERE
    Calc.c = (SELECT MAX(c) FROM Calc calc2
              WHERE Calc.id = Calc2.id)
ORDER BY
    id

Es ist nicht am kürzesten. Aber ich denke, dass es für SQL sehr flexibel ist. Es ist einfach, neue Operatoren hinzuzufügen. Es ist einfach, die Priorität der Bediener zu ändern.

Bleis-Tift
quelle
1
Meine Güte, ich glaube nicht, dass ich jemals eine SQL-Lösung erwartet habe! Dies ist nicht ganz im Sinne von Code Golf, aber es wurde trotzdem für die Kühnheit gestimmt (und nicht einmal mit einer Programmiersprache). :)
Noldorin
2
@Noldorin, warum ist es nicht im Sinne von Code Golf?
Tuinstoel
6

F #

Anzahl der Zeichen: 327

OP suchte nach einer F # -Version, hier ist sie. Kann viel schöner gemacht werden, da ich hier einen Schiedsrichter missbrauche , um Charaktere zu speichern. Es behandelt die meisten Dinge wie - (1.0) , 3 - -3 und sogar 0 - .5 usw.

let g s=
 let c=ref[for x in System.Text.RegularExpressions.Regex.Matches(s,"[0-9.]+|[^\s]")->x.Value]
 let rec e v=if (!c).IsEmpty then v else 
  let h=(!c).Head
  c:=(!c).Tail
  match h with|"("->e(e 0.0)|")"->v|"+"->e(v+(e 0.0))|"-"->e(v-(e 0.0))|"/"->e(v/(e 0.0))|"*"->e(v*(e 0.0))|x->float x
 e(e 0.0)
thr
quelle
In der Tat hoffte ich auf eine F # -Lösung. Dank dafür. Die Anzahl der Zeichen ist auch ziemlich anständig, insbesondere wenn man bedenkt, dass "System.Text.RegularExpressions.Regex.Matches" eine absurde Anzahl von Zeichen einnimmt.
Noldorin
Ja, das gleiche gilt für die Aufrufe .Value.IsEmpty / Tail / Head. Ich habe eine neue Version in Arbeit. Ich hoffe auf unter 250 Zeichen.
thr
Ich bin mir nicht sicher, ob Sie in einigen Code-Golf-Wettbewerben Anweisungen außerhalb der Zeichenanzahl importieren / verwenden dürfen. Das würde definitiv helfen, wenn ja. :) Ich freue mich auf die neue Version.
Noldorin
@Noldorin: Nein, es tut mir leid, dass ich es nicht unter die 327 Zeichen dieses (seit dem letzten Mal etwas verbesserten) Codes bekommen kann. Der Gewinn, wenn alles perfekt mit dem regulären Ausdruck analysiert wird, überwiegt den wahnsinnig langen Namen "System.Text.RegularExpressions.Regex.Matches". Wenn F # einen kurzen (Alias-) Namen für die Matches-Funktion gehabt hätte, wäre ich bei 288 Zeichen. aber es ist nicht = /.
thr
@fredrikholmstrom: Keine Sorge - trotzdem gute Lösung. Ich bin mir auch nicht ganz sicher, aber ich würde sagen, dass Sie "System.Text.RegularExpressions" in eine "open" -Anweisung verschieben und zumindest die Anzahl der Zeichen dafür ausschließen sollten.
Noldorin
6

J.

Anzahl der Zeichen: 208

Nach Jeff Mosers Kommentar wurde mir klar, dass ich diese Sprache völlig vergessen hatte ... Ich bin kein Experte, aber mein erster Versuch verlief ziemlich gut.

e=:>@{:@f@;:
f=:''&(4 :0)
'y x'=.x g y
while.($y)*-.')'={.>{.y do.'y x'=.(x,>(-.'/'={.>{.y){('%';y))g}.y end.y;x
)
g=:4 :0
z=.>{.y
if.z='('do.'y z'=.f}.y else.if.z='-'do.z=.'_',>{.}.y end.end.(}.y);":".x,z
)

Es ist ein bisschen nervig, abbilden zu müssen x/yund -zin J's x%yund _z. Ohne das könnten vielleicht 50% dieses Codes verschwinden.

kurzlebig
quelle
Ja, das ist ziemlich nett. Was ist nun mit einer Lösung in K? : P Ich vermute, dass das sogar Perl schlagen könnte.
Noldorin
Woohoo, ich habe es geschafft, meine Haskell-Lösung unter meine J-Lösung zu bekommen! Wenn jemand hier ein J-, K- oder APL-Zauberer wäre, würde er wahrscheinlich die 200-Zeichen-Barriere zerstören ...
kurzlebig
6

Python (ohne etwas zu importieren)

Anzahl der Zeichen: 222

Ich habe viele Tricks aus Daves Antwort gestohlen, aber ich habe es geschafft, einige weitere Charaktere zu rasieren.

def e(s,l=0,n=0,f='+'):
 if s:l=[c for c in s+')'if' '!=c]
 while f!=')':
  p=l.pop;m=p(0)
  if m=='(':m=e(0,l)
  while l[0]not in'+-*/)':m+=p(0)
  m=float(m);n={'+':n+m,'-':n-m,'*':n*m,'/':n/(m or 1)}[f];f=p(0)
 return n

Kommentierte Version:

def evaluate(stringexpr, listexpr=0, n=0, f_operation='+'):
    # start out as taking 0 + the expression... (or could use 1 * ;)

    # We'll prefer to keep the expression as a list of characters,
    # so we can use .pop(0) to eat up the expression as we go.
    if stringexpr:
        listexpr = [c for c in stringexpr+')' if c!=' ']

    # use ')' as sentinel to return the answer
    while f_operation != ')':
        m_next = listexpr.pop(0)
        if m_next == '(':
            # lists are passed by reference, so this call will eat the (parexp)
            m_next = evaluate(None, listexpr)

        else:
            # rebuild any upcoming numeric chars into a string
            while listexpr[0] not in '+-*/)':
                m_next += listexpr.pop(0)

        # Update n as the current answer.  But never divide by 0.
        m = float(m_next)
        n = {'+':n+m, '-':n-m, '*':n*m, '/':n/(m or 1)}[f_operation]

        # prepare the next operation (known to be one of '+-*/)')
        f_operation = listexpr.pop(0)

    return n
krubo
quelle
+1 Schöne Diktieridee. Die aktuelle Version schlägt jedoch bei e ('1 + 0') fehl. Verwenden Sie stattdessen {'+': n + m, '-': nm, ' ': n m, '/': n / m, wenn m sonst 1}. Ich habe Ihre Idee ausgeliehen (mit diesem Änderungsantrag). Danke
stephan
Vielen Dank. Ich hatte nicht an das DivZero-Problem gedacht. Ein 7-Zeichen-Fix ist n / (m oder 1).
Krubo
Werde das auch für mein Programm machen ;-)
stephan
hehe, ändere jetzt nichts, die Anzahl der Charaktere ist wunderschön :)
Tetha
5

C #

Anzahl der Zeichen: 403

Also hier ist meine Lösung ... Ich warte immer noch darauf, dass jemand eine in C # veröffentlicht, die sie schlagen kann. (Marc Gravell war nah dran und kann es nach etwas mehr Basteln noch besser machen als ich.)

Vollständig verschleierte Funktion:

float e(string x){float v=0;if(float.TryParse(x,out v))return v;x+=';';int t=0;
char o,s='?',p='+';float n=0;int l=0;for(int i=0;i<x.Length;i++){o=s;if(
x[i]!=' '){s=x[i];if(char.IsDigit(x[i])|s=='.'|(s=='-'&o!='1'))s='1';if(s==')')
l--;if(s!=o&l==0){if(o=='1'|o==')'){n=e(x.Substring(t,i-t));if(p=='+')v+=n;
if(p=='-')v-=n;if(p=='*')v*=n;if(p=='/')v/=n;p=x[i];}t=i;if(s=='(')t++;}
if(s=='(')l++;}}return v;}

Halbverschleierte Funktion:

public static float Eval(string expr)
{
    float val = 0;
    if (float.TryParse(expr, out val))
        return val;
    expr += ';';
    int tokenStart = 0;
    char oldState, state = '?', op = '+';
    float num = 0;
    int level = 0;
    for (int i = 0; i < expr.Length; i++)
    {
        oldState = state;
        if (expr[i] != ' ')
        {
            state = expr[i];
            if (char.IsDigit(expr[i]) || state == '.' ||
                (state == '-' && oldState != '1'))
                state = '1';
            if (state == ')')
                level--;
            if (state != oldState && level == 0)
            {
                if (oldState == '1' || oldState == ')')
                {
                    num = Eval(expr.Substring(tokenStart, i - tokenStart));
                    if (op == '+') val += num;
                    if (op == '-') val -= num;
                    if (op == '*') val *= num;
                    if (op == '/') val /= num;
                    op = expr[i];
                }
                tokenStart = i;
                if (state == '(')
                    tokenStart++;
            }
            if (state == '(')
                level++;
        }
    }
    return val;
}

Hier ist nichts allzu Kluges los, wie es scheint. Die Funktion hat jedoch den Vorteil, dass sie wieder eintritt (dh threadsicher).

Ich bin auch ziemlich zufrieden mit der Anzahl der Zeichen, da es in C # geschrieben ist (gültig 1.0, 2.0 und 3.0, glaube ich).

Noldorin
quelle
Alle Tipps, wie ich die Anzahl der Zeichen weiter reduzieren könnte, wären willkommen. (Das ist mein erster wirklicher Versuch, Code Golf zu spielen.)
Noldorin
Ich habe es <400 bekommen, aber es schlägt den bearbeiteten Test fehl, den Sie hinzugefügt haben ;-p
Marc Gravell
Vorschläge: "var" für float, char - rasiert nur einige wenige und verliert jedoch die C # 1.2 / 2.0-Kompatibilität.
Marc Gravell
@Marc: Ja, das ist ungefähr so ​​weit wie ich auch gekommen bin. Mit ein paar anderen kleinen Änderungen könnte ich es auf 390 reduzieren, aber nicht weniger.
Noldorin
Schöne Lösung Nolorin. Ich konnte Ihre Lösung auf 361
Chris Persichetti
5

Hier kommt noch einer:

Shell-Skript (mit sed + awk)

Anzahl der Zeichen: 295

verschleiert:

e(){ a="$1";while echo "$a"|grep -q \(;do eval "`echo "$a"|sed 's/\(.*\)(\([^()]*\))\(.*\)/a="\1\`e \"\2\"\`\3"/'`";done; echo "$a"|sed 's/\([-+*/]\) *\(-\?\) */ \1 \2/g'|awk '{t=$1;for(i=2;i<NF;i+=2){j=$(i+1);if($i=="+") t+=j; else if($i=="-") t-=j; else if($i=="*") t*=j; else t/=j}print t}';}

lesbar

e () {
    a="$1"
    # Recursively process bracket-expressions
    while echo "$a"|grep -q \(; do
        eval "`echo "$a"|
            sed 's/\(.*\)(\([^()]*\))\(.*\)/a="\1\`e \"\2\"\`\3"/'`"
    done
    # Compute expression without brackets
    echo "$a"|
        sed 's/\([-+*/]\) *\(-\?\) */ \1 \2/g'|
        awk '{
            t=$1;
            for(i=2;i<NF;i+=2){
                j=$(i+1);
                if($i=="+") t+=j;
                else if($i=="-") t-=j;
                else if($i=="*") t*=j;
                else t/=j
            }
            print t
        }'
}

Prüfung:

str='  2.45 / 8.5  *  9.27   +    (   5   *  0.0023  ) '
echo "$str"|bc -l
e "$str"

Ergebnis:

2.68344117647058823526
2.68344
Soulmerge
quelle
Ich habe (fast) keine Ahnung, wie das funktioniert, aber ich bin erstaunt, wie gut ein Shell-Skript bei dieser Aufgabe funktioniert! In der Tat gut gemacht.
Noldorin
Denken Sie
5

MATLAB (v7.8.0)

Anzahl der Zeichen: 239

Verschleierte Funktion:

function [v,s]=m(s),r=1;while s,s=regexp(s,'( ?)(?(1)-?)[\.\d]+|\S','match');c=s{end};s=[s{1:end-1}];if any(c>47),v=str2num(c);elseif c>41,[l,s]=m(s);v=[l/v l*v l+v l-v];v=v(c=='/*+-');if r,break;end;r=1;elseif c<41,break;end;r=r&c~=41;end

Löschfunktion (er):

function [value,str] = math(str)
  returnNow = 1;
  while str,
    str = regexp(str,'( ?)(?(1)-?)[\.\d]+|\S','match');
    current = str{end};
    str = [str{1:end-1}];
    if any(current > 47),
      value = str2num(current);
    elseif current > 41,
      [leftValue,str] = math(str);
      value = [leftValue/value leftValue*value ...
               leftValue+value leftValue-value];
      value = value(current == '/*+-');
      if returnNow,
        break;
      end;
      returnNow = 1;
    elseif current < 41,
      break;
    end;
    returnNow = returnNow & (c ~= 41);
  end

Prüfung:

>> [math('1 + 3 / -8'); ...
math('2*3*4*5+99'); ...
math('4 * (9 - 4) / (2 * 6 - 2) + 8'); ...
math('1 + ((123 * 3 - 69) / 100)'); ...
math('2.45/8.5*9.27+(5*0.0023)')]

ans =

   -0.5000
  219.0000
   10.0000
    4.0000
    2.6834

Synopsis: Eine Mischung aus regulären Ausdrücken und Rekursion. So ziemlich das Beste, was ich bisher konnte, ohne zu schummeln und EVAL zu benutzen.

gnovice
quelle
5

Rubin

Anzahl der Zeichen: 170

Verschleiert:

def s(x)
while x.sub!(/\(([^\(\)]*?)\)/){s($1)}
x.gsub!('--','')
end
while x.sub!(/(-?[\d.]+)[ ]*([+\-*\/])[ ]*(-?[\d.]+)/){$1.to_f.send($2,$3.to_f)}
end
x.strip.to_f
end

Lesbar:

def s(x)
while x.sub!(/\(([^\(\)]*?)\)/){s($1)}
x.gsub!('--','')
end
while x.sub!(/(-?[\d.]+)[ ]*([+\-*\/])[ ]*(-?[\d.]+)/){$1.to_f.send($2,$3.to_f)}
end
x.strip.to_f
end

[
  ['1 + 3 / -8', -0.5],
  ['2*3*4*5+99', 219],
  ['4 * (9 - 4) / (2 * 6 - 2) + 8', 10],
  ['1 + ((123 * 3 - 69) / 100)', 4],
  ['2.45/8.5*9.27+(5*0.0023)',2.68344117647059],
  ['(3+7) - (5+2)', 3]
].each do |pair|
  a,b = s(String.new(pair[0])),pair[1]
  print pair[0].ljust(25), ' = ', b, ' (', a==b, ')'
  puts
end

Es gibt keine wirkliche Verschleierung zu dieser, die ich frisch gepostet habe, da sie sich stark von meiner ersten unterscheidet. Ich hätte das von Anfang an sehen sollen. Der Prozess ist ein sehr einfacher Eliminierungsprozess: Suchen und lösen Sie das höchste Klammerpaar (das am meisten verschachtelte) in eine Zahl, bis keine weiteren mehr gefunden werden, und lösen Sie dann alle vorhandenen Zahlen und Operationen im Ergebnis auf. Und während ich Aussagen in Klammern auflöse, werden alle Doppelstriche entfernt (Float.to_f weiß nicht, was ich damit anfangen soll).

Es unterstützt also positive und negative Zahlen (+3, 3, -3) und sogar negierte Unterausdrücke in der Klammer nur in der Reihenfolge der Verarbeitung. Die einzige kürzere Implementierung ist die Perl-Implementierung (ohne Bewertung).

Bearbeiten: Ich bin immer noch auf der Jagd nach Perl, aber dies ist derzeit die zweitkleinste Antwort. Ich habe es mit Änderungen an der zweiten Regex und durch Ändern der Behandlung der Zeichenfolge als destruktiv verkleinert (ersetzt die alte Zeichenfolge). Dadurch musste die Zeichenfolge nicht mehr dupliziert werden. Ich stellte fest, dass dies nur ein neuer Zeiger auf die Zeichenfolge ist. Beim Umbenennen der Funktion in s von lösen wurden einige Zeichen gespeichert.

Robert K.
quelle
Gute Arbeit, überrascht, dass ich diesen Ansatz nicht selbst ausprobiert habe, da ich etwas sehr Ähnliches verwendet habe, um eine andere Analysefrage zu lösen.
Mike Tunnicliffe
In meiner Lösung finden Sie eine Möglichkeit, diesen regulären Ausdruck zu komprimieren. Sie sollten auch nicht den letzten "Streifen" brauchen. Und es sieht nicht so aus, als würden Sie unäres Minus vollständig implementieren, sodass Sie vom gsub ('-', '') nur wenig profitieren.
Finnw
Ich kann meinen speziellen Algorithmus nicht wirklich verkürzen oder ich habe 3-4 der Tests nicht bestanden, ich bin mir nicht sicher warum. Ich könnte es aber um vielleicht 20 Zeichen verkleinern.
Robert K
4

Python mit regulären Ausdrücken

Anzahl der Zeichen: 283

Vollständig verschleierte Funktion:

import re
from operator import*
def c(e):
 O=dict(zip("+-/*()",(add,sub,truediv,mul)))
 a=[add,0];s=a
 for v,o in re.findall("(-?[.\d]+)|([+-/*()])",e):
  if v:s=[float(v)]+s
  elif o=="(":s=a+s
  elif o!=")":s=[O[o]]+s
  if v or o==")":s[:3]=[s[1](s[2],s[0])]
 return s[0]

Nicht verschleiert:

import re
from operator import *

def compute(s):
    operators = dict(zip("+-/*()", (add, sub, truediv, mul)))
    stack = [add, 0]
    for val, op in re.findall("(-?[.\d]+)|([+-/*()])", s):
        if val:
            stack = [float(val)] + stack
        elif op == "(":
            stack = [add, 0] + stack
        elif op != ")":
            stack = [operators[op]] + stack
        if val or op == ")":
            stack[:3] = [stack[1](stack[2], stack[0])]
    return stack[0]

Ich wollte sehen, ob ich die anderen Python-Lösungen mit regulären Ausdrücken schlagen kann.

Konnte nicht.

Der reguläre Ausdruck, den ich verwende, erstellt eine Liste von Paaren (val, op), in denen nur ein Element in jedem Paar gültig ist. Der Rest des Codes ist ein eher standardmäßiger stapelbasierter Parser mit einem tollen Trick, die obersten 3 Zellen im Stapel durch das Ergebnis der Berechnung unter Verwendung der Python-Listenzuweisungssyntax zu ersetzen. Damit dies mit negativen Zahlen funktioniert, sind nur zwei zusätzliche Zeichen erforderlich (-? In der Regex).

gooli
quelle
Sie können einige Bytes speichern, indem Sie "()" aus Ihrer Operatorzeichenfolge entfernen. zipstoppt am Ende der kürzeren Liste.
Ben Blank
@gooli: Benutzt du Windows? Nach meiner Zählung beträgt die veröffentlichte Lösung nur 273. Eine Erklärung dafür könnte sein, dass Sie Zeilenumbrüche als jeweils zwei Zeichen gezählt haben. (Python ist es egal, ob Sie Zeilenumbrüche mit einem Zeichen haben, auch unter Windows.) Eine andere Erklärung ist, dass Sie 8 getroffen haben, als Sie 7 meinten ;;)
John Y
4

Python

Anzahl der Zeichen: 382

Eine weitere Python-Lösung, bei der häufig reguläre Ausdrücke ersetzt werden. Bei jedem Durchlauf durch die Schleife werden die einfachsten Ausdrücke berechnet und die Ergebnisse wieder in die Zeichenfolge eingefügt.

Dies ist der nicht verschleierte Code, es sei denn, Sie betrachten reguläre Ausdrücke als verschleiert.

import re
from operator import *    
operators = dict(zip("+-/*", (add, sub, truediv, mul)))    
def compute(s):
    def repl(m):
        v1, op, v2 = m.groups()
        return str(operators[op](float(v1), float(v2)))
    while not re.match("^\d+\.\d+$", s):
        s = re.sub("([.\d]+)\s*([+-/*])\s*([.\d]+)", repl, s)
        s = re.sub("\(([.\d]+)\)", r"\1", s)
    return s

Hatte diese Idee gerade als ich mich umdrehte und konnte sie nicht loslassen, bis ich sie aufgeschrieben und zum Laufen gebracht hatte.

Elifiner
quelle
1
Schöne Lösung ... Sieht auch für mich sehr klar aus. Es scheint, dass die Verwendung von dict / zip zum Speichern der Operatoren in Python definitiv ein sehr effektiver Ansatz ist.
Noldorin
4

C #

Anzahl der Zeichen: 396 (aktualisiert)

(aber der Test, den Sie mit "/ -8" hinzugefügt haben, schlägt fehl, und ich bin nicht geneigt, ihn zu beheben ...

static float Eval(string s){int i,j;s=s.Trim();while((i=s.IndexOf(')'))>=0){j=s.LastIndexOf('(',i,i);s=s.Substring(0,j++)+Eval(s.Substring(j,i-j))+s.Substring(i+1);}if((i=s.LastIndexOfAny("+-*/".ToCharArray()))<0) return float.Parse(s);var r=float.Parse(s.Substring(i+1));var l=i>0?Eval(s.Substring(0,i)):(float?)null;return s[i]=='+'?(l??0)+r:(s[i]=='-'?(l??0)-r:(s[i]=='/'?(l??1)/r:(l??1)*r));}

Von:

static float Eval(string s)
{
    int i, j;
    s = s.Trim();
    while ((i = s.IndexOf(')')) >= 0)
    {
        j = s.LastIndexOf('(', i, i);
        s = s.Substring(0, j++) + Eval(s.Substring(j, i - j)) + s.Substring(i + 1);
    } 
    if ((i = s.LastIndexOfAny("+-*/".ToCharArray())) < 0) return float.Parse(s);
    var r = float.Parse(s.Substring(i + 1));
    var l = i > 0 ? Eval(s.Substring(0, i)) : (float?)null;
    return s[i] == '+'
        ? (l ?? 0) + r
        : (s[i] == '-'
            ? (l ?? 0) - r
            : (s[i] == '/'
                ? (l ?? 1) / r
                : (l ?? 1) * r));
}
Marc Gravell
quelle
Ah wunderbar, eine C # -Lösung. Insbesondere Ihre Verwendung von nullbaren Typen ist sehr interessant. 484 scheint ziemlich gut zu sein, da Sie nicht die Zeit hatten, es aufzuräumen. (Eine Verbesserung wäre, glaube ich, die switch-Anweisung in eine Reihe von ifs umzuwandeln.) Ich habe jetzt meine eigene C # -Lösung veröffentlicht, wenn Sie vergleichen möchten. :)
Noldorin
4

Python

Anzahl der Zeichen: 235

Vollständig verschleierte Funktion:

def g(a):
 i=len(a)
 while i:
  try:m=g(a[i+1:]);n=g(a[:i]);a=str({'+':n+m,'-':n-m,'*':n*m,'/':n/(m or 1)}[a[i]])
  except:i-=1;j=a.rfind('(')+1
  if j:k=a.find(')',j);a=a[:j-1]+str(g(a[j:k]))+a[k+1:]
 return float(a.replace('--',''))

Halbverschleiert:

def g(a):
    i=len(a);
    # do the math
    while i:
        try:
            # recursively evaluate left and right
            m=g(a[i+1:])
            n=g(a[:i])
            # try to do the math assuming that a[i] is an operator
            a=str({'+':n+m,'-':n-m,'*':n*m,'/':n/(m or 1)}[a[i]])
        except:
            # failure -> next try
            i-=1
            j=a.rfind('(')+1
        # replace brackets in parallel (this part is executed first)
        if j:
            k=a.find(')',j)
            a=a[:j-1]+str(g(a[j:k]))+a[k+1:]
    return float(a.replace('--',''))

FWIW, die n + 1. Python-Lösung. Bei einem offensichtlichen Missbrauch von try - außer ich verwende einen Trial-and-Error-Ansatz. Es sollte alle Fälle richtig behandeln, einschließlich Sachen wie -(8), --8und g('-(1 - 3)'). Es ist wieder eintretend. Ohne Unterstützung für den --Fall, den viele Implementierungen nicht unterstützen, liegt der Wert bei 217 Zeichen (siehe vorherige Überarbeitung).

Vielen Dank für eine interessante Stunde an einem Sonntag und weitere 30 Minuten am Montag. Danke an krubo für sein nettes dikt.

stephan
quelle
Ein weiterer interessanter Ansatz ... Identisch in der Länge auch mit einer der anderen Python-Lösungen. Dies bestätigt meine Ansicht, dass die Verwendung eines Wörterbuchs von Operatoren nach Möglichkeit der richtige Weg ist. Ich wollte etwas Ähnliches in C # machen, aber die Syntax nimmt einfach zu viele Zeichen auf.
Noldorin
4

Rubin

Anzahl von Charakteren: 217 179

Dies ist die bisher kürzeste Rubinlösung (eine stark auf RegExp basierende Lösung liefert falsche Antworten, wenn die Zeichenfolge nur wenige Gruppen von Klammern enthält).-- Nicht mehr wahr. Lösungen, die auf Regex und Substitution basieren, sind kürzer. Dieser basiert auf einem Stapel von Akkumulatoren und analysiert den gesamten Ausdruck von links nach rechts. Es ist wiedereintrittsfähig und ändert die Eingabezeichenfolge nicht. Es könnte beschuldigt werden, gegen die Regeln der Nichtverwendung verstoßen zu haben eval, da es FloatMethoden mit identischen Namen wie ihre mathematischen Mnemoniken (+, -, /, *) aufruft .

Verschleierter Code (alte Version, unten optimiert) :

def f(p);a,o=[0],['+']
p.sub(/-/,'+-').scan(/(?:(-?\d+(?:\.\d+)?)|(.))\s*/).each{|n|
q,w=n;case w;when'(';a<<0;o<<'+';when')';q=a.pop;else;o<<w
end if q.nil?;a[-1]=a[-1].method(o.pop).call(q.to_f) if !q.nil?};a[0];end

Mehr verschleierter Code:

def f(p);a,o=[0],[:+]
p.scan(/(?:(-?\d+(?:\.\d+)?)|(.))\s*/).each{|n|q,w=n;case w
when'(';a<<0;o<<:+;when')';q=a.pop;else;o<<w;end if !q
a<<a.pop.send(o.pop,q.to_f)if q};a[0];end

Code reinigen:

def f(p)
  accumulators, operands = [0], ['+']
  p.gsub(/-/,'+-').scan(/(?:(-?\d+(?:\.\d+)?)|(.))\s*/).each do |n|
    number, operand = n
    case operand
      when '('
        accumulators << 0
        operands << '+'
      when ')'
        number = accumulators.pop
        operands.pop
      else 
        operands[-1] = operand
    end if number.nil?
    accumulators[-1] = accumulators.last.method(operands[-1]).call(number.to_f) unless number.nil?
  end
  accumulators.first
end
Samuil
quelle
Tatsächlich ist meine kürzer (198) und verwendet Regex, um Klammern von oben nach unten vor dem Endergebnis der Mathematik zu lösen. "3 + (3 * (3 + 9))" lautet also: "3 + (3 * 12)", "3 + 36", 39. Es geht von oben nach unten, von links nach rechts. Es löst alle Tests mit Ausnahme eines kleinen Versehens, das Leerzeichen zwischen Token erfordert. Siehe: stackoverflow.com/questions/928563/…
Robert K
Nicht dass deine nicht klug ist, es ist sehr viel.
Robert K
(3 + 7) - (5 + 2) - das habe ich mit mehreren Klammergruppen gemeint. Ihre Lösung hat aufgrund der Regex-Gier ein Problem mit nicht verschachtelten Klammern.
Samuil
Das mag sein, aber ich hatte gestern Abend mit meinem Parser herumgespielt und ihn auf meinem System verbessert (mit mathematischen Funktionen und Einzelbuchstabenvariablen). Also habe ich meinen besseren regulären Ausdruck daraus gezogen und es funktioniert einwandfrei. Der Beitrag wird auch mit einer neuen Zeichenanzahl aktualisiert. ;-) Ich werde deine Gleichung in der Antwort in die Tests einfließen lassen.
Robert K
Ich denke nicht, dass die Verwendung von 'Methode' oder 'Senden' betrügt - es ist nur eine Tabellensuche - Sie verwenden nicht den eingebauten Parser.
Finnw
3

Ruby 1.8.7

Anzahl der Zeichen: 620

Versuchen Sie, meine Implementierung zu vereinfachen. Es ist das erste Mal in meinem Leben, dass ich einen Ausdrucksparser schreibe! Ich garantiere, dass es nicht das Beste ist.

Verschleiert:

def solve_expression(e)
t,r,s,c,n=e.chars.to_a,[],'','',''
while(c=t.shift)
n=t[0]
if (s+c).match(/^(-?)[.\d]+$/) || (!n.nil? && n.match(/\d/) && c=='-')
s+=c
elsif (c=='-' && n=='(') || c=='('
m,o,x=c=='-',1,''
while(c=t.shift)
o+=1 if c=='('
o-=1 if c==')'
x+=c unless c==')' && o==0
break if o==0
end
r.push(m ? -solve_expression(x) : solve_expression(x))
s=''
elsif c.match(/[+\-\/*]/)
r.push(c) and s=''
else
r.push(s) if !s.empty?
s=''
end
end
r.push(s) unless s.empty?
i=1
a=r[0].to_f
while i<r.count
b,c=r[i..i+1]
c=c.to_f
case b
when '+': a=a+c
when '-': a=a-c
when '*': a=a*c
when '/': a=a/c
end
i+=2
end
a
end

Lesbar:

def solve_expression(expr)
  chars = expr.chars.to_a # characters of the expression
  parts = [] # resulting parts
  s,c,n = '','','' # current string, character, next character

  while(c = chars.shift)
    n = chars[0]
    if (s + c).match(/^(-?)[.\d]+$/) || (!n.nil? && n.match(/\d/) && c == '-') # only concatenate when it is part of a valid number
      s += c
    elsif (c == '-' && n == '(') || c == '(' # begin a sub-expression
      negate = c == '-'
      open = 1
      subExpr = ''
      while(c = chars.shift)
        open += 1 if c == '('
        open -= 1 if c == ')'
        # if the number of open parenthesis equals 0, we've run to the end of the
        # expression.  Make a new expression with the new string, and add it to the
        # stack.
        subExpr += c unless c == ')' && open == 0
        break if open == 0
      end
      parts.push(negate ? -solve_expression(subExpr) : solve_expression(subExpr))
      s = ''
    elsif c.match(/[+\-\/*]/)
      parts.push(c) and s = ''
    else
      parts.push(s) if !s.empty?
      s = ''
    end
  end
  parts.push(s) unless s.empty? # expression exits 1 character too soon.

  # now for some solutions!
  i = 1
  a = parts[0].to_f # left-most value is will become the result
  while i < parts.count
    b,c = parts[i..i+1]
    c = c.to_f
    case b
      when '+': a = a + c
      when '-': a = a - c
      when '*': a = a * c
      when '/': a = a / c
    end
    i += 2
  end
  a
end
Robert K.
quelle
Das ist ziemlich gut für einen ersten Versuch, und die Länge ist sowieso nicht sehr weit von den anderen entfernt. Natürlich ist der Algorithmus ziemlich klar. Beachten Sie, dass Sie die Anzahl der Zeichen erheblich reduzieren können, indem Sie Variablennamen aus einem Buchstaben verwenden!
Noldorin
Vielen Dank. Es dauerte eine Weile, bis mein letzter Fehler behoben war, aber im Allgemeinen war es nichts, was mich um den Verstand brachte. Zum Glück funktioniert es voll.
Robert K
3

Ruby 1.9

(wegen der Regex)

Anzahl der Zeichen: 296

def d(s)
  while m = s.match(/((?<pg>\((?:\\[()]|[^()]|\g<pg>)*\)))/)
    s.sub!(m[:pg], d(m[:pg][1,m[:pg].size-2]))
  end
  while m = s.match(/(-?\d+(\.\d+)?)\s*([*+\-\/])\s*(-?\d+(\.\d+)?)/)
    r=m[1].to_f.send(m[3],m[4].to_f) if %w{+ - * /}.include?m[3]
    s.sub!(m[0], r.to_s)
  end
  s
end

BEARBEITEN: Beinhaltet Martins Optimierung.

Daniel Huckstep
quelle
r = m [1] .to_f.send (m [3], m [4] .to_f) wenn% w {+ - * /}.include?m[3]
Martin Carpenter
Noch besser! Ich habe versucht, mir einen guten Weg zu überlegen, und das hat mich überrumpelt.
Daniel Huckstep
3

SNOBOL4

Anzahl der Zeichen: 232

        a = pos(0) | '('
        n = span('0123456789.')
        j = '!+;!-;!*;!/;       output = e'
d       j '!' len(1) . y = "    e a . q n . l '" y "' n . r = q (l " y " r)     :s(p)"  :s(d)
        k = code(j)
        e = input
s       e ' ' = :s(s)
p       e ('(' n . i ')') = i   :s(p)f<k>
end

Dies ist ein Halbbetrüger. Es verwendet code()(eine Variante von eval), um sich selbst zu dekomprimieren, aber nicht, um den Eingabeausdruck auszuwerten.

Entdeckte Version, ohne code:

        prefix = pos(0) | '('
        num = span('0123456789.')
        expr = input
spaces  expr ' ' = ''   :s(spaces)
paren   expr ('(' num . x ')') = x      :s(paren)
add     expr (prefix . pfx) (num . l) '+' (num . r) = pfx (l + r)       :s(paren)
sub     expr (prefix . pfx) (num . l) '-' (num . r) = pfx (l - r)       :s(paren)
mul     expr (prefix . pfx) (num . l) '*' (num . r) = pfx (l * r)       :s(paren)
div     expr (prefix . pfx) (num . l) '/' (num . r) = pfx (l / r)       :s(paren)
        output = expr
end

Strategie:

  • Entfernen Sie zuerst alle Leerzeichen ( spaces)
  • Entfernen Sie nach Möglichkeit Klammern um eine Zahl ( paren)
  • Suchen Sie andernfalls einen einfachen Ausdruck mit zwei Zahlen, denen '('oder am Anfang der Zeichenfolge vorangestellt wird
  • Wenn keine der oben genannten Regeln gilt, wird der Ausdruck vollständig ausgewertet. Wenn die Eingabe nun gut geformt war, sollten wir eine Zahl hinterlassen.

Beispiel:

  • 1 + (2 * 3) + 4
  • 1+(2*3)+4[ spaces]
  • 1+(6)+4[ mul]
  • 1+6+4[ paren]
  • 7+4[ add]
  • 11[ add]
finnw
quelle
3

C #

Anzahl der Zeichen: 355

Ich nahm Noldorins Antwort und änderte sie, also gib Noldorin 99% der Gutschrift dafür. Das Beste, was ich mit dem verwendeten Algorithmus machen konnte, waren 408 Zeichen. Siehe Noldorins Antwort für die klarere Codeversion.

Änderungen vorgenommen:
Ändern Sie die Zeichenvergleiche, um sie mit den Zahlen zu vergleichen.
Einige Standarddeklarationen wurden entfernt und derselbe Deklarationstyp kombiniert.
Einige der if-Anweisungen wurden überarbeitet.

float q(string x){float v,n;if(!float.TryParse(x,out v)){x+=';';int t=0,l=0,i=0;char o,s='?',p='+';for(;i<x.Length;i++){o=s;if(x[i]!=32){s=x[i];if(char.IsDigit(x[i])|s==46|(s==45&o!=49))s='1';if(s==41)l--;if(s!=o&l==0){if(o==49|o==41){n=q(x.Substring(t,i-t));v=p==43?v+n:p==45?v-n:p==42?v*n:p==47?v/n:v;p=x[i];}t=i;if(s==40)t++;}if(s==40)l++;}}}return v;}

Bearbeiten: Es wurde von 361 auf 355 weiter heruntergefahren, indem eine der Rückgabeanweisungen entfernt wurde.

Knusprig
quelle
Ah, ich wusste nicht, dass Sie es bereits als neue Antwort gepostet haben. Vielen Dank für all die Anerkennung (was wahrscheinlich mehr ist, als ich verdient habe, da ich um 390 feststeckte). Ich werde mir die Änderungen bald genauer ansehen ... das einzige, was ich in Betracht gezogen habe, war, Zeichenvergleiche zu ändern, um Zahlen zu verwenden. :)
Noldorin