评估一串简单的mathexpression式

挑战

这是挑战(我自己的发明,尽pipe如果以前在网上其他地方出现过,我不会感到惊讶)。

编写一个函数,该函数接受一个简单的mathexpression式的string表示forms的单个参数,并将其评估为浮点值。 “简单expression式”可以包括以下任何一项:正数或负数小数, +*/ 。 expression式使用(正常)中缀表示法 。 运营商应该按照它们出现的顺序进行评估,也就是说, 应该像BODMAS那样进行评估 ,当然,应该正确观察括号。 该函数应该返回此表单的任何可能expression式的正确结果。 但是,该函数不必处理格式错误的expression式(即语法错误的expression式)。

expression式的例子:

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

规则

我预计在这里会出现某种forms的“作弊”/诡诈,所以请让我提前警告它! 通过作弊,我指的是在dynamic语言(如JavaScript或PHP)中使用eval或等效函数,或同时编译和执行代码。 (我认为我的“不BODMAS”的说明几乎保证了这一点)。除此之外,没有任何限制。 我预计在这里有一些正则expression式的解决scheme,但看到更多的不仅仅是这些。

现在,我主要对C#/ .NET的解决scheme感兴趣,但其他语言也是完全可以接受的(特别是对于function/混合方法的F#和Python)。 我还没有决定是否接受最短或最巧妙的解决scheme(至less是语言)作为答案,但是我欢迎任何forms的解决scheme ,除了我刚才禁止的!

我的解决scheme

现在我已经发布了我的C#解决scheme(403个字符)。 更新: 我的新解决scheme已经击败了294个字符 ,在一个可爱的正则expression式的帮助下显着! 我怀疑这会被一些语言轻松打败,语法较轻(尤其是function/dynamic语言),并且已经被certificate是正确的,但是我还是很好奇,如果有人能够在C#中打败这个。

更新

我已经看到了一些非常狡猾的解决scheme。 感谢所有发布过的人。 虽然我还没有testing过其中的任何一个,但是我会相信别人,并且认为他们至less可以使用所有的例子。

只是为了说明,重入(即线程安全) 不是该function的要求,虽然这是一个奖金。


格式

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

语言

字符数:???

完全混淆function:

 (code here) 

清除/半混淆function:

 (code here) 

任何有关algorithm/巧妙的快捷方式的笔记。


Perl(没有评估)

字符数: 167 106 (请参阅下面的106个字符的版本)

完全混淆function:(如果将这三行join一行,则为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

考虑到ephedient的编辑,现在可以减less到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

下面的ruby解决scheme正在推动我进一步,但我不能达到它的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($_):$_} 

我不得不放弃并使用'' 。 这个rubysend技巧对于这个问题是非常有用的。

从石头上挤水

字符数: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($_):$_} 

这是这个函数的testing工具:

 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' 

汇编

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 

编辑: Unobfuscated来源:

  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 

ruby

字符数:103

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

这是“邪恶的跳蚤”解决scheme的非recursion版本。 括号内的子expression式是自下而上而不是自上而下的。

编辑 :将'while'转换为条件+尾recursion已经保存了几个字符,所以它不再是非recursion的(尽piperecursion在语义上是不必要的)。

编辑 :借用丹尼尔马丁的合并正则expression式保存另外11个字符!

编辑 :这个recursion比我第一次想到的更有用! 如果x碰巧包含单个数字,则x.to_f可以被重写为e(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 

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 :if; 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";} 

Visual Basic.NET

字符数:9759

我更像是自己的保龄球。

注:不考虑嵌套括号。 另外,未经testing,但我很确定它的工作原理。

 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 

哈斯克尔

字符数: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(sa)$choice(zipWith(\c o->char c>>return(o$sa))"+-*/"[(+),(-),(*),(/)])>>=setState>>t 

而现在,真正进入高尔夫精神的3条线和182个字节:

 q=snd.(`e`id).filter(' '/=) esc|[(f,h)]<-readsPrec 0 s=gh(cf);e('(':s)c=gh(cf)where(')':h,f)=es id g('+':h)=e h.(+);g('-':h)=e h.(-);g('*':h)=e h.(*);g('/':h)=e h.(/);gh=(,)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) 

python

字符数:237

完全混淆function:

 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 

清除/半混淆function:

 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 

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=gh 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=gh 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 

Notes This edited version is rather more evil than my first attempt. Same algorithm, but now inline with a horrible tangle of goto s. I've ditched the co-routines, but am now using a couple of flavors of computed branches. All error checking and reporting has been removed, but this version will silently recover from some classes of unexpected characters in the input. This version also compiles with g77.

The primary limits are still fortran's rigid formatting, long and ubiquitous keywords, and simple primitives.

C99

Number of characters: 239 (But see below for 209 )

compressed function:

 #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?ab:o==42?a*b:a/b;}}F f(char*x){e=x;return v();} 

decompressed function:

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

Function is not re-entrant.

EDIT from Chris Lutz : I hate to trample on another man's code, but here is a 209 -character 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:aX:a+X;}} #define f(x) (e=x,v()) 

Readable (well, not really very readable, but decompressed):

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

Yeah, f() is a macro, not a function, but it works. The readable version has some of the logic rewritten but not reordered (like o != '+' instead of o - '+' ), but is otherwise just an indented (and preprocessed) version of the other one. I keep trying to simplify the if(!o|o==41)return a; part into the for() loop, but it never makes it shorter. I still believe it can be done, but I'm done golfing. If I work on this question anymore, it will be in the language that must not be named .

Common Lisp

(SBCL)
Number of characters: 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 (gx))))(mapcar(lambda(xy)(setf m(apply x(list my))))on)m)))(defun w(e)(g(read-from-string(concatenate'string"("e")")))) 

Proper version (387 chars):

 (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 (xy) (setf mem (apply x (list mem y)))) op-list num-list) mem))) 

Input is form w() , which takes one string argument. It uses the trick that nums/operands and operators are in the pattern NONON … and recursively evaluates all operands, and therefore getting nesting very cheap. ;)

JavaScript (Not IE compatible)

Number of characters: 268/260

Fully obfuscated function:

 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=='-'?ab:o=='*'?a*b:a/b}}return E()} 

or, in JavaScript 1.8 (Firefox 3+), you can save a few characters by using expression closures:

 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=='-'?ab:o=='*'?a*b:a/b}},E()) 

Clear/semi-obfuscated function:

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

Neither version will work in IE, because they use array-style subscripting on the string. If you replace both occurrences of x[0] with x.charAt(0) , the first one should work everywhere.

I cut out some more characters since the first version by turning variables into function parameters and replacing another if statement with the conditional operator.

C# with Regex Love

Number of characters: 384

Fully-obfuscated:

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

Not-obfuscated:

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

Takes advantage of .NET's Regex balancing group feature .

PHP

Number of characters: 284

obfuscated:

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

readable:

 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) 

Should work with any valid input (including negative numbers and arbitrary whitespace)

SQL (SQL Server 2008)

Number of characters: 4202

Fully obfuscated function:

 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=Si-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 

Clear/semi-obfuscated function:

 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 = Si - 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 

It is not shortest. But I think that it is very flexible for SQL. It's easy to add new operators. It's easy to change priority of operators.

F#

Number of characters: 327

OP was looking for an F# version, here it is. Can be done a lot nicer since I'm abusing a ref here to save characters. It handles most things such as -(1.0) , 3 – -3 and even 0 – .5 etc.

 let gs= let c=ref[for x in System.Text.RegularExpressions.Regex.Matches(s,"[0-9.]+|[^\s]")->x.Value] let rec ev=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) 

Ĵ

Number of characters: 208

After Jeff Moser 's comment, I realized that I had completely forgotten about this language… I'm no expert, but my first attempt went rather well.

 e=:>@{:@f@;: f=:''&(4 :0) 'y x'=.xgy 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 ) 

It's a bit annoying, having to map x/y and -z into J's x%y and _z . Without that, maybe 50% of this code could disappear.

Python (without importing anything)

Number of characters: 222

I stole many tricks from Dave's answer, but I managed to shave off some more characters.

 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,'-':nm,'*':n*m,'/':n/(m or 1)}[f];f=p(0) return n 

Commented 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, '-':nm, '*':n*m, '/':n/(m or 1)}[f_operation] # prepare the next operation (known to be one of '+-*/)') f_operation = listexpr.pop(0) return n 

C#

Number of characters: 403

So here's my solution… I'm still waiting for someone to post one in C# that can beat it. (Marc Gravell was close, and may yet do better than me after some more tinkering.)

Fully obfuscated function:

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

Semi-obfuscated function:

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

Nothing too clever going on here, it woul seem. The function does however have the advantage of being re-entrant (ie thread-safe).

I am also reasonably pleased with the number of chars, given that it's written in C# (valid 1.0, 2.0, and 3.0 I believe).

Here comes another one:

Shell script (using sed+awk)

Number of characters: 295

obfuscated:

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

readable

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

testing:

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

结果:

 2.68344117647058823526 2.68344 

MATLAB (v7.8.0)

Number of characters: 239

Obfuscated function:

 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/vl*v l+v lv];v=v(c=='/*+-');if r,break;end;r=1;elseif c<41,break;end;r=r&c~=41;end 

Clear(er) function:

 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 

testing:

 >> [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: A mixture of regular expressions and recursion. Pretty much the best I have been able to do so far, without cheating and using EVAL.

ruby

Number of characters: 170

Obfuscated:

 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 

Readable:

 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 

There is no real obfuscation to this one, which I decided to post fresh since it's wildly different from my first. I should have seen this from the start. The process is a very simple process of elimination: find and resolve the highest pair of parenthesis (the most nested) into a number until no more are found, then resolve all the existing numbers and operations into the result. And, while resolving parenthetical statements I have it strip all double-dashes (Float.to_f doesn't know what to do with them).

So, it supports positive and negative numbers (+3, 3, & -3) and even negated sub-expressions within the parenthesis just by the order of processing. The only shorter implementation is the Perl (w/o eval) one.

Edit: I'm still chasing Perl, but this is the second smallest answer right now. I shrunk it with changes to the second regex and by changing the treatment of the string to be destructive (replaces the old string). This eliminated the need to duplicate the string, which I found out to just be a new pointer to the string. And renaming the function to s from solve saved a few characters.

Python with regular expressions

Number of characters: 283

Fully obfuscated function:

 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] 

Not obfuscated:

 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] 

I wanted to see if I cab beat the other Python solutions using regular expressions.

Couldn't.

The regular expression I'm using creates a list of pairs (val, op) where only one item in each pair is valid. The rest of the code is a rather standard stack based parser with a neat trick of replacing the top 3 cells in the stack with the result of the computation using Python list assignment syntax. Making this work with negative numbers required only two additional characters (-? in the regex).

python

Number of characters: 382

Yet another Python solution, heavily using regular expression replacement. Each run through the loop the simplest expressions are computed and the results are put back into the string.

This is the unobfuscated code, unless you consider regular expressions to be obfuscated.

 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 

Had this idea just as I was turning in and couldn't let it go until I wrote it down and made it work.

C#

Number of characters: 396 (updated)

(but fails the test you added with "/ -8", and I'm not inclined to fix it…

 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,ij))+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)); } 

python

Number of characters: 235

Fully obfuscated function:

 def g(a): i=len(a) while i: try:m=g(a[i+1:]);n=g(a[:i]);a=str({'+':n+m,'-':nm,'*':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('--','')) 

Semi-obfuscated:

 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,'-':nm,'*':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, the n+1th Python solution. In a blatant abuse of try-except I use a trial-and-error approach. It should handle all cases properly including stuff like -(8) , --8 and g('-(1 - 3)') . It is re-entrant. Without support for the -- case which many implementations don't support, it is at 217 chars (see previous revision).

Thanks for an interesting hour on a Sunday and another 30 mins on Monday. Thanks to krubo for his nice dict.

ruby

Number of characters: 217 179

This is the shortest ruby solution up to now (one heavily based on RegExp yields incorrect answers when string contains few groups of parenthesis) — no longer true. Solutions based on regex and substitution are shorter. This one is based on stack of accumulators and parses whole expression from left to right. It is re-entrant, and does not modify input string. It could be accused of breaking the rules of not using eval , as it calls Float 's methods with identical names as their mathematical mnemonics (+,-,/,*).

Obfuscated code (old version, tweaked below) :

 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 

More obfuscated 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 

Clean code:

 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 

Ruby 1.8.7

Number of characters: 620

Do try and take it easy on my implementation, it's the first time I've written an expression parser in my life! I guarantee that it isn't the best.

Obfuscated:

 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=ac when '*': a=a*c when '/': a=a/c end i+=2 end a end 

Readable:

 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 

Ruby 1.9

(because of the regex)

Number of characters: 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 

EDIT: Includes Martin's optimization.

SNOBOL4

Number of characters: 232

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

This is a semi-cheat. It uses code() (a variant of eval) to de-compress itself, but not to evaluate the input expression.

De-obfuscated version, without 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 

Strategy:

  • First, remove all spaces ( spaces )
  • Whenever possible, remove parentheses surrounding a number ( paren )
  • Otherwise, find a simple expression involving two numbers, prefixed by '(' or at the start of the string
  • If none of the above rules apply, the expression is fully evaluated. Now if the input was well formed we should be left with a number.

例:

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

C#

Number of Characters: 355

I took Noldorin's Answer and modified it, so give Noldorin 99% of the credit for this. Best I could do with the algorithm was using was 408 characters. See Noldorin's Answer for the clearer code version.

Changes made:
Change char comparisons to compare against numbers.
Removed some default declarations and combined same type of declarations.
Re-worked some of the if statments.

 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,it));v=p==43?v+n:p==45?vn:p==42?v*n:p==47?v/n:v;p=x[i];}t=i;if(s==40)t++;}if(s==40)l++;}}}return v;} 

Edit: knocked it down some more, from 361 to 355, by removing one of the return statments.