评估一串简单的数学表达式


76

挑战

这是我自己的发明所面临的挑战,尽管如果它以前出现在网络上的其他地方,我也不会感到惊讶。

编写一个函数,该函数接受单个参数,该参数是简单数学表达式的字符串表示形式,并将其评估为浮点值。“简单表达式”可以包括以下任意值:正,负十进制数+-*/。表达式使用(普通)中缀表示法。尽管应该正确观察括号,但是应该按照出现的顺序对操作员进行评估,即不像BODMAS中那样。该函数应为任何返回正确的结果这种形式的可能表达。但是,该函数不必处理格式错误的表达式(即语法错误的表达式)。

表达式示例:

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...

规则

我期望在这里有某种形式的“作弊” /狡猾,所以请让我预先警告一下!通过作弊,我指的是eval在动态语言(例如JavaScript或PHP)中使用或等效功能,或者即时编译和执行代码。(不过,我认为我的“ no BODMAS”规范已经对此做出了保证。)除此之外,没有任何限制。我期待在这里找到一些Regex解决方案,但不仅仅可以看到更多。

现在,我主要对这里的C#/。NET解决方案感兴趣,但是任何其他语言也完全可以接受(特别是对于功能/混合方法,是F#和Python)。我尚未决定是否要接受最短或最巧妙的解决方案(至少对于语言而言)作为答案,但是我欢迎任何语言的任何形式的解决方案,但上面我刚刚禁止的方式除外!

我的解决方案

我现在在这里发布了我的C#解决方案(403个字符)。更新: 在一些可爱的正则表达式的帮助下,我的新解决方案294个字符大大击败了旧解决方案!我怀疑这很容易被较轻的语法(特别是函数/动态语言)所击败,并且已经被证明是正确的,但是我很好奇是否有人还能在C#中击败它。

更新资料

我已经看到了一些非常狡猾的解决方案。感谢所有发表过的人。尽管我还没有测试过它们中的任何一个,但我还是要相信人们,并假设他们至少可以使用所有给定的示例。

仅需注意,重入(即线程安全)不是该功能的要求,尽管这是一个奖励。


格式

为了便于比较,请以以下格式发布所有答案:

语言

字符数:???

完全模糊的功能:

(code here)

清除/半混淆功能:

(code here)

关于算法/智能快捷键的所有注释。



2
您可能是说您的第一个示例等于.125(移动小数位),第二个示例在左侧具有99(太多的九)。
约翰Y

是的,谢谢。那些是非常明显的错别字。
诺多林

1
您应该添加一个示例,其中缺少BODMAS的情况非常严重,例如“ 1 + 1 * 3 = 6”
Ben Blank

3
啊,我想知道何时结束第一次投票。给所有选民的注意:在StackOverflow中已经有很多公开代码高尔夫问题。共识似乎是他们很好-主要只是一点点乐趣。
诺多林

3
我倾向于同意这很好,尤其是作为“ wiki”
Marc Gravell

Answers:


36

Perl(无评估)

字符数:167 106(见下文为106字符版本)

完全模糊的功能:(如果将这三行合并为一个,则为167个字符)

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){}$_}

清除/模糊化版本:

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
  $_
}

我最初误读了规则,因此提交了带有“ eval”的版本。这是一个没有它的版本。

洞察最新位来到时,我意识到,在字符代码的最后一个八进制数字+-/*是不同的,而且ord(undef)是0。这让我建立了调度表@a作为一个数组,只是调用的代码位置7 & ord($3)

还有一个明显的地方可以剃掉另外一个角色-q""换成''-但这将使其更难剪切并粘贴到外壳中。

甚至更短

字符数:124 106

考虑到临时的编辑,现在减少到124个字符:(将两行合并为一个)

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;$_}

更短

字符数:110 106

尽管无法达到其104个字符,但下面的红宝石解决方案使我进一步前进:

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

我不得不屈服和使用''。这个红宝石send把戏对于这个问题确实有用。

从石头里挤水

字符数:106

为避免除零检查而作的小变形。

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

这是此功能的测试工具:

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'

3
Perl的体积很小,这非常吓人,我编辑了答案,使它保持最小的Ruby实现,并以170个字符的空间耗尽。但是124?好肉汁!
罗伯特·K

1
我没有注意到还没有人提及它,但是此解决方案需要Perl 5.10。为了与5.8兼容,请使用(-?(?> \ d +(\。\ d +)?)),该字符长两个字符。
短暂

7
@Epaga,别担心我弄错了你的错:Perl。是。太棒了
丹尼(Danny)

1
将其缩短1个字符-将“ $ _ = $ _ [0]”更改为“($ _)= @ _”。
克里斯·卢茨

1
因为它不考虑运算符而无条件地执行算术运算(以后再选择正确的结果),所以它确实需要避免除以零。
短暂

44

组装工

427字节

混淆,与出色的A86组装成.com可执行文件:

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

编辑:混淆源:

        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

1
汇编-这是真实的编程!
Andreas Rejbrand

我曾经看过一个完整的俄罗斯方块游戏,大小为64个字节
BlueRaja-Danny Pflughoeft 2010年

29

红宝石

字符数: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

这是The Wicked Flea解决方案的非递归版本。括号内的子表达式是自下而上而不是自上而下的。

编辑:将“ while”转换为条件+尾递归已保存了一些字符,因此不再是非递归的(尽管递归在语义上不是必需的。)

编辑:借用Daniel Martin合并正则表达式的想法可以节省另外11个字符!

编辑:该递归比我最初想到的还要有用!如果恰好包含一个数字,x.to_f可以将其重写为。e(x)x

编辑:使用“ or”代替“ ||”可以删除一对括号。

长版:

# 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

我几乎以为这是新的领导者,直到我看到Perl编辑器变得更短了!辛苦了
Noldorin

1
像其他解决方案一样,摆脱'e = readline.chomp; ...; p e.to_f'并使用'def q(e); ...; e.to_f; end'可以节省10个字符。但是,如问题所示,它无法达到q(“ 1 + 3 / -8”)==-0.5。
短暂

@ephemient是您发现的错误-无法处理负数。
finnw

1
我的代码中的gsub!('-','')表示如果否定括号参数的工作方式。如果负括号的内部结果为负,则语句外的负号仍为:--7.0。但是,支持该功能需要花费我24个字符,但仍然比您高19个字符。除了从您那里学到的窍门,我不知道还能缩水什么。(但我做了第二次尝试就很棒!)
罗伯特·K

1
使用“发送”确实接近违反“禁止评估”规则。但是,将空格合并到数字正则表达式中的妙招。使用这个技巧,另一个使我的perl解决方案减少到119个字符。
Daniel Martin 2009年

29

C(VS2005)

字符数:1360

滥用预处理器和警告进行有趣的代码布局(向下滚动以查看):

#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";}

25

Visual Basic.NET

字符数:9759

我自己更像是一个圆顶硬礼帽。

注意:不考虑嵌套括号。另外,未经测试,但我敢肯定它可以工作。

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

2
如果不是很晚的话,我也可能会打出1万个字符:)
杰森(Jason)2009年

你知道少一些字符更好吗?这样,他们永远不会认为vb.net有用。
Ikke

11
@ikke-应该是尽可能少的字符?噢,亲爱的...有人似乎错过了重点
杰森

3
ZERO_STRING_REPRESENTATION看起来像是上thedailywtf属于
erikkallen

1
+1使我笑得比其他任何答案都要多。曾经
R .. GitHub停止帮助ICE,2010年

25

哈斯克尔

字符数:182

不尝试聪明,只做一些压缩:4行,312字节。

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

现在,真正进入高尔夫精神的是3行和182字节的数据:

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

爆炸:

-- 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)

我怀疑仍然有可能跌破225,但这是我凌晨3点所能达到的。
迅速

这似乎是一个很好的功能解决方案。(如果没有这些注释,我当然是不会理解的,因此,谢谢您。)此外,您目前仅领先于Dave的Python解决方案,因此您似乎处于领先地位!我很好奇F#解决方案是否可以匹配甚至击败它。
诺多林

对我来说很有趣的是,即使尝试了最小化,Parsec解决方案(解析器组合器=广义正则表达式+更多)也无法接近手动解析。我认为F#的语法不能像Haskell一样简洁,但我也欢迎竞争:)
短暂

@ephemient:我尝试在Python中同时使用正则表达式和解析器模块。几乎低于300个字符,但没有机会获得竞争优势。问题是导入,函数调用吃得太多。大多数语言(Perl除外)都是如此。顺便说一句,正如我的解决方案所示,您不必解析自己就可以大大低于300个字符。
斯蒂芬

2
我认为您已经过度惩罚了角色数量。该问题要求使用String-> Double函数,因此您应该通过替换“ main = interact $ show”来计算字符。与“Q =”,为17个字符,把你的计数在209
丹尼尔·马丁

23

蟒蛇

字符数:237

完全模糊的功能:

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

清除/半混淆功能:

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

注意:当前版本虽然可以正确处理否定文字,但不能处理-(1.0)之类的输入。从规范中尚不清楚是否需要这样做。
戴夫

通过将l塞入e的参数列表中,可以免费使l非全局。但是,仍然不是线程安全的。
戴夫

非常狡猾。进行解释很值得。:)
尼克·约翰逊,

@Dave:我的也失败-(1.0)了,所以不用担心!我会澄清这个问题。无论如何,看起来很聪明的解决方案-尽管我仍在尝试弄清楚它是如何工作的(不完全了解Python)。如果您可以添加一个简短的解释,将不胜感激。
诺多林

20

Fortran 77(gfortran方言,现在支持g77)

字符数: 2059

混淆版本:

      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

清晰版:(3340个字符,带支架)

      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

注释此编辑版本比我的第一次尝试更加邪恶。相同的算法,但是现在内联到gotos的可怕缠结。我已经放弃了协程,但是现在使用了几种计算分支的方式。所有错误检查和报告均已删除,但此版本将从输入中某些意外字符类别中静默恢复。该版本还可以与g77一起编译。

主要限制仍然是fortran的固定格式,冗长而普遍存在的关键字以及简单的原语。


14
天哪,老兄!你今天一定很无聊。;)
gnovice

2
呵呵,我认为我从来没有期待过Fortran解决方案!我想我们可以得出结论,该语言不是特别适合于编码高尔夫吗?无论如何,都是为了纯粹的努力和使用过时的语言而投票。:)
Noldorin

我发现在Fortran中这种轻率的字节拖累是冗长而笨拙的,但实际上并不难。另一方面,编写非结构化代码并使用这些计算出的分支感觉有些古怪。
dmckee ---前主持人小猫,

做得很好,但是与我的简短的ruby1.9小版本相比,具有2000字符以上字符的fortran版本如何获得更多的选票?大声笑
Daniel Huckstep 09年

@darkhelmet:我不知道。我做得很傻,期望一两票就可以使他变得更加努力和变态。我为这种可恶感到自豪,但这太荒谬了……
dmckee ---前主持人小猫

17

C99

字符数:239(但请参见下面的209

压缩功能:

#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();}

解压功能:

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();
}

功能不可重入。

克里斯·卢茨(Chris Lutz)的编辑:我讨厌践踏另一个人的代码,但这是209个字符的版本:

#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())

可读(嗯,不是很可读,但是解压缩了):

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())

是的,它f()是一个宏,而不是一个函数,但是可以工作。可读版本具有一些已重写但未重新排序的逻辑(例如o != '+'而不是o - '+'),但在其他方面只是一个缩进(和预处理)的版本。我一直在尝试简化循环if(!o|o==41)return a;部分for(),但绝不会使其变得更短。我仍然相信可以做到,但是我已经打高尔夫球了。如果我再处理这个问题,它将使用不得使用的语言命名


不错的解决方案,以及使用“纯” C的加分。此外,我的打败3个字符!重新入场不是规则,所以很好。(但是,这是一个加号。)
Noldorin,2009年

真好!您可以通过使用ASCII码删除更多的字符,例如用48替换'0',等等。当然,您可以使用atof()而不是您自己的浮动解析器来保存一大堆,但是有意不使用库函数,这不是对该问题的严格要求。
亚当·罗森菲尔德2009年

我当时在考虑使用atof(),但它不会告诉您float字符串在哪里结束,因此无论如何您都需要解析它。
费鲁乔(Ferruccio)

谢谢小费,亚当。使用该方法和其他一些(丑陋的)技巧,我将其缩小了一些。
费鲁吉欧(Ferruccio)

uch,我没有指望负数。代码膨胀为400个字符。
费鲁吉欧(Ferruccio)

13

普通Lisp

(SBCL)
字符数: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")"))))

正确的版本(387个字符):

(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)))

输入是form w(),它接受一个字符串参数。它使用数字/操作数和运算符处于NONON ...模式的技巧,并递归计算所有操作数,因此嵌套非常便宜。;)


聪明的解决方案。尽管如此,鉴于该规范是针对该函数采用字符串对象的,所以我不确定它是否完全有效。
诺多林

对于那个很抱歉。固定!

没问题。没意识到转换是如此的容易。好的解决方案,还是!
Noldorin

哇。太好了 :)
Emil H 2009年

11

JavaScript(不兼容IE)

字符数:268/260

完全模糊的功能:

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()}

或者,在JavaScript 1.8(Firefox 3+)中,您可以使用表达式闭包保存一些字符:

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())

清除/半混淆功能:

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();
}

两种版本都无法在IE中使用,因为它们在字符串上使用数组样式下标。如果更换两次出现的x[0]x.charAt(0),第一个应该在任何地方工作。

自第一个版本以来,我通过将变量转换为函数参数并用条件运算符替换了另一个if语句,减少了一些字符。


很好 我正在等待有人在这里使用正则表达式。:)看来动态语言绝对可以解决这个问题。
诺多林

8

C#与Regex Love

字符数: 384

完全混淆:

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;}

不混淆:

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;
}

利用.NET的Regex平衡组功能


感谢您的解决方案。:)我不确定我是否会看到使用regex的C#解决方案,但是在这里有。现在,是否应该包括“ using System.Text.RegularExpressions;”是有争议的。在您的字符数中,但这仍然是一个很好的解决方案。
诺多林

那不是规则的一部分:)。如果添加“使用R = System.Text.RegularExpressions.Regex;” 并取代我的“正则表达式”与R,它进入417
杰夫·莫泽

@Jeff:从技术上讲,没有using语句是不会编译的,因此默认情况下应该包含它。但是,考虑到我们的C#解决方案都远远落后于领导者,这很重要。
诺多林

8

的PHP

字符数:284

混淆:

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);}

可读性:

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)

应该与任何有效输入(包括负数和任意空格)一起使用


preg_replace()使用e修饰符可以为您节省更多的字节。
Alix Axel 2012年

8

SQL(SQL Server 2008)

字符数:4202

完全模糊的功能:

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

清除/半混淆功能:

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

这不是最短的。但是我认为它对于SQL非常灵活。添加新的运算符很容易。更改操作员的优先级很容易。


1
天哪,我不认为我曾期望过SQL解决方案!这并不是完全按照代码高尔夫的精神进行的,但是无论如何还是为了大胆而投票(甚至不使用编程语言)。:)
Noldorin

2
@Noldorin,为什么不遵循代码高尔夫的精神?
tuinstoel

6

F#

字符数:327

OP正在寻找F#版本,就在这里。可以做得更好,因为我在这里滥用引用保存字符。它处理大多数事情,例如-(1.0)3--3甚至0-.5等。

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)

确实,我希望有一个F#解决方案。感谢那。字符数也相当不错,尤其是考虑到“ System.Text.RegularExpressions.Regex.Matches”占用的字符数非常荒谬。
诺多林

是的,与.Value.IsEmpty / Tail / Head调用相同-我在工作中得到了一个新版本; p希望获得250个以下字符。
THR

我实际上不确定在某些代码高尔夫比赛中是否允许在字符计数之外导入/使用语句。如果有的话,那肯定会有所帮助。:)期待看到新版本。
诺多林

@Noldorin:不,很抱歉,在此代码的327个字符下(自上次以来有所增强),我无法理解它。通过用正则表达式完美解析所有内容所获得的收益超过了“ System.Text.RegularExpressions.Regex.Matches”的疯狂长名。如果F#的Matches函数有一个简短的别名,我将为288个字符,但不是= /。
THR

@fredrikholmstrom:不用担心-好的解决方案。另外,我也不完全确定,但是我想说,您应该能够将“ System.Text.RegularExpressions”移动到“ open”语句中,并至少排除该字符数。
诺多林

6

Ĵ

字符数:208

Jeff Moser发表评论之后,我意识到我已经完全忘记了这种语言……我不是专家,但是我的第一次尝试进行得相当顺利。

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
)

这是一个有点恼人,不得不映射x/y-z成J的x%y_z。否则,此代码可能会消失50%。


是的,那很好。现在,在K中的解决方案呢?:P我怀疑它甚至可以击败Perl。
Noldorin

哇,我设法将我的Haskell解决方案放在我的J解决方案下!尽管如果这里有人是J,K或APL向导,他们可能会破坏200个字符的屏障...
短暂

6

Python(不导入任何内容)

字符数:222

我从Dave的答案中偷了很多花样,但我设法剃掉了更多的角色。

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

评论版本:

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

+1好字典的想法。但是,当前版本在e('1 + 0')上失败。如果使用m else 1} ,请使用{'+':n + m,'-':nm,' ':n m,'/':n / m。我已经借用了您的想法(此修正案)。谢谢
Stephan

谢谢。我没有想到过DivZero问题。7个字符的修正为n /(m或1)。
krubo

也将为我的程序执行此操作;-)
Stephan

呵呵,现在什么都不要更改,字符的数量很漂亮:)
Tetha

5

C#

字符数:403

因此,这就是我的解决方案...我仍在等待有人发布可以击败它的C#代码。(马克·格雷弗尔(Marc Gravell)距离很近,经过一番修补后,它的表现可能会比我好。)

完全模糊的功能:

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;}

半模糊功能:

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;
}

似乎没有什么聪明的事情发生。但是,该函数确实具有可重入的优点(即线程安全)。

鉴于字符数是用C#编写的(我相信有效的1.0、2.0和3.0),我也对字符数感到满意。


欢迎提供有关如何进一步减少字符数的任何提示。(这是我第一次真正的尝试打高尔夫。)
诺多林,

我得到了它<400,但它未通过您添加的编辑测试;-p
Marc Gravell

建议:对于浮点数,char,“ var”表示-只刮了一些,但是失去了C#1.2 / 2.0兼容性。
Marc Gravell

@Marc:是的,这也差不多。进行一些其他小的更改,我可能会将其降低到390,但不少。
诺多林

很好的解决方案Nolorin。我能够将您的解决方案降低到361
Chris Persichetti,2009年

5

再来一个:

Shell脚本(使用sed + awk)

字符数:295

混淆:

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}';}

可读的

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
        }'
}

测试:

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

结果:

2.68344117647058823526
2.68344

我几乎不知道它是如何工作的,但是我很惊讶shell脚本在此任务中的表现如何!确实做得很好。
Noldorin

好吧,请记住,许多操作系统都使用该语言/工具来完成许多不同的任务:)
soulmerge

5

MATLAB(v7.8.0)

字符数:239

混淆函数:

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

清除功能:

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

测试:

>> [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

简介:正则表达式和递归的混合物。到目前为止,我能做的最好的事情就是不作弊和使用EVAL。


5

红宝石

字符数:170

混淆:

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

可读性:

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

对此并没有真正的困惑,我决定发布它,因为它与我的第一个完全不同。我应该从一开始就看到这一点。该过程是一个非常简单的消除过程:找到最高括号对(最嵌套的)并将其解析为一个数字,直到找不到更多为止,然后将所有现有的数字和运算解析为结果。而且,在解析括号中的语句时,我将其删除了所有双破折号(Float.to_f不知道如何处理它们)。

因此,仅按处理顺序,它就在括号内支持正数和负数(+3、3和-3)甚至负数子表达式。唯一较短的实现是Perl(不带评估)。

编辑:我仍在追逐Perl,但这是目前第二小的回答。我通过更改第二个正则表达式并通过将字符串的处理方式更改为破坏性的方式来缩小了它(替换了旧的字符串)。这样就消除了重复字符串的需要,我发现这只是指向该字符串的新指针。和重命名功能,小号解决保存几个字符。


很好的工作,很惊讶我没有亲自尝试这种方法,因为我使用了非常相似的方法来解决另一个解析问题。
Mike Tunnicliffe

请参阅我的解决方案以了解一种压缩该正则表达式的方法。您也不需要最后的“条”。而且看起来好像您没有完全实现一元减号,所以您从gsub('-','')中获得的收益很小。
finnw

我实际上无法缩短我的特定算法,或者我未能通过3-4次测试,我不确定为什么。我可以缩小20个字符。
罗伯特·K

4

带有正则表达式的Python

字符数:283

完全模糊的功能:

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]

不混淆:

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]

我想看看我是否使用正则表达式击败了其他Python解决方案。

不能。

我正在使用的正则表达式会创建一个成对的列表(val,op),其中每对中只有一个有效。其余代码是一个相当标准的基于堆栈的解析器,巧妙地用Python列表分配语法将堆栈的前3个单元替换为计算结果。使用负数进行此操作仅需要两个附加字符(正则表达式中的-?)。


您可以通过从运算符字符串中删除“()”来节省几个字节。zip停在较短列表的末尾。
本·布兰克

@gooli:您正在使用Windows吗?根据我的统计,发布的解决方案只有273个。对此的一种解释可能是您将换行符算作两个字符。(Python不在乎您是否具有单字符换行符,即使在Windows中也是如此。)另一种解释是,当您的意思是7时,请打8;)
John Y

4

蟒蛇

字符数:382

另一个Python解决方案,大量使用正则表达式替换。每次循环运行时,都会计算最简单的表达式,然后将结果放回字符串中。

这是未混淆的代码,除非您认为正则表达式已混淆。

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

就在我上交这个想法的时候,直到我写下来并付诸实践,才让它走了。


1
不错的解决方案...我也很清楚。似乎使用dict / zip存储运算符绝对是Python中非常有效的方法。
诺多林

4

C#

字符数:396(更新)

(但未通过您用“ / -8”添加的测试,因此,我不愿意对其进行修复...

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));}

从:

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));
}

太好了,这是C#解决方案。您对可空类型的使用尤其有趣。鉴于您没有时间整理它,484似乎还不错。(我相信一个改进就是将switch语句转换为一系列的if。)如果您希望比较的话,我现在发布了自己的C#解决方案。:)
Noldorin

4

蟒蛇

字符数:235

完全模糊的功能:

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('--',''))

半模糊:

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,第n + 1个Python解决方案。在公然地尝试尝试中,我使用了试错法。它应该处理所有情况下,此包括像的东西-(8)--8g('-(1 - 3)')。它是可重入的。如果不支持--许多实现不支持的情况,则为217个字符(请参阅以前的版本)。

感谢您在星期日进行有趣的一小时,而在星期一又进行了30分钟。感谢krubo的出色表现


另一个有趣的方法...在长度上也与其他Python解决方案相同。这证实了我的观点,即在可能的情况下使用运算符字典是必经之路。我想在C#中做类似的事情,但是语法只占用了太多字符。
诺多林

4

红宝石

字符数: 217 179

这是迄今为止最短的红宝石解决方案(当字符串包含少量括号时,一个基于RegExp的解决方案会产生错误的答案)-不再如此。基于正则表达式和替换的解决方案较短。这是基于累加器堆栈,并从左到右解析整个表达式。它是可重入的,并且不会修改输入字符串。它可能被指控违反了不使用的规则eval,因为它Float以与它们的数学助记符(+,-,/,*)相同的名称调用的方法。

混淆的代码(旧版本,在下面进行了调整)

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

更混淆的代码:

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

干净的代码:

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

实际上,我的较短(198),并使用正则表达式在数学最终结果之前自上而下地解决了括号问题。因此,“ 3 +(3 *(3 + 9))”变为:“ 3 +(3 * 12)”,“ 3 + 36”,39。它从上到下,从左到右。它解决了所有测试,除了一个小的监督需要在令牌之间留有空格。请参阅:stackoverflow.com/questions/928563/...
罗伯特ķ

不是说你的并不聪明,而是很多。
罗伯特·K

(3 + 7)-(5 + 2)-这就是几个括号组的意思。由于正则表达式贪婪,您的解决方案的非嵌套括号有问题。
samuil

可能是这样,但是昨天晚上我一直在摆弄我的解析器,并在我的系统上对其进行了改进(具有数学函数和单字母变量)。因此,我从中取出了更好的正则表达式,并且效果很好,该帖子也使用了新的字符数进行了更新。;-)我将把您的方程式暂时加入答案中的测试中。
罗伯特·K

我不认为使用“方法”或“发送”是作弊的-只是表查找-您没有使用内置的解析器。
finnw

3

Ruby 1.8.7

字符数:620

尝试并轻松实现它,这是我一生中第一次编写表达式解析器!我保证这不是最好的。

混淆:

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

可读性:

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

对于第一次尝试来说,这是相当不错的,而且它的长度也不比其他的要大。当然,该算法非常清晰。请注意,仅使用一个字母的变量名就可以大大减少字符数!
Noldorin

谢谢。我的上一个错误花了一段时间才能解决,但总的来说并没有什么麻烦的事。幸运的是,它可以正常运行。
罗伯特·K

3

Ruby 1.9

(由于正则表达式)

字符数: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

编辑:包括马丁的优化。


如果%w {+-* /}.include?m[3],则r = m [1] .to_f.send(m [3],m [4] .to_f)
Martin Carpenter,2009年

更好!我试图想办法做到这一点,但我却没想到。
Daniel Huckstep 09年

3

SNOBOL4

字符数: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

这是一个半作弊。它使用code()(eval的一种变体)对其自身进行解压缩,但不评估输入表达式。

消除混淆的版本,不包含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

战略:

  • 首先,删除所有空格(spaces
  • 尽可能删除数字(paren)周围的括号
  • 否则,找到一个包含两个数字的简单表达式'(',在字符串的开头或开头
  • 如果以上规则均不适用,则表达式将被完全求值。现在,如果输入格式正确,我们应该留下一个数字。

例:

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

3

C#

字符数:355

我选择了Noldorin的答案并对其进行了修改,因此,为此给了Noldorin 99%的信誉。我所能使用的最佳算法是408个字符。有关更清晰的代码版本,请参见Noldorin的答案

进行的更改:
更改char比较以与数字进行比较。
删除了一些默认声明,并合并了相同类型的声明。
重新设计了一些if陈述。

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;}

编辑:通过删除一项返回陈述将其从361降低到355。


啊,我不知道您已经将它发布为新答案。感谢所有的功劳(这可能比我应得的要多,因为我被困在390左右)。我将在不久的将来更加仔细地查看这些修改...我考虑的唯一修改是将char比较更改为使用数字。:)
Noldorin
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.