我挑战你写一个数学表达式评估器,它尊重PEMDAS(操作顺序:括号,取幂,乘法,除法,加法,减法)而不使用正则表达式,一个预先存在的“Eval()”函数,解析库等
我在SO(here)上看到了一个预先存在的评估者挑战,但那个特别需要从左到右的评估。
示例输入和输出:
"-1^(-3*4/-6)" -> "1"
"-2^(2^(4-1))" -> "256"
"2*6/4^2*4/3" -> "1"
我在C#中编写了一个评估员,但是希望看到它与那些用他们选择的语言的智能程序员相比有多糟糕。
澄清:
让我们这个函数接受一个字符串参数并返回一个字符串结果。
至于为什么没有正则表达式,那就是平衡竞争环境。我认为对于“最紧凑的正则表达式”应该有一个单独的挑战。
使用StrToFloat()是可以接受的。通过“解析库”,我的意思是排除诸如通用语法解析器之类的东西,也用于平衡游戏场。
支持花车。
支持paretheses,取幂和四个算术运算符。
赋予乘法和除法优先权。
赋予加法和减法相同的优先权。
为简单起见,您可以假设所有输入都是格式良好的。
我没有偏好你的函数是否接受“.1”或“1e3”之类的东西作为有效数字,但是接受它们会获得布朗尼积分。 ;)
对于除零情况,您可能会返回“NaN”(假设您希望实现错误处理)。
答案 0 :(得分:27)
答案 1 :(得分:16)
C#,13行:
static string Calc(string exp)
{
WebRequest request = WebRequest.Create("http://google.com/search?q=" +
HttpUtility.UrlDecode(exp));
using (WebResponse response = request.GetResponse())
using (Stream dataStream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(dataStream))
{
string r = reader.ReadToEnd();
int start = r.IndexOf(" = ") + 3;
int end = r.IndexOf("<", start);
return r.Substring(start, end - start);
}
}
这会压缩到大约317个字符:
static string C(string e){var q = WebRequest.Create("http://google.com/search?q="
+HttpUtility.UrlDecode(e));using (var p=q.GetResponse()) using (var s=
p.GetResponseStream()) using (var d = new StreamReader(dataStream)){var
r=d.ReadToEnd();var t=r.IndexOf(" = ") + 3;var e=r.IndexOf("<",t);return
r.Substring(t,e-t);}}
击> <击> 撞击>
感谢Mark和P Daddy的评论,压缩到195个字符:
string C(string f){using(var c=new WebClient()){var r=c.DownloadString
("http://google.com/search?q="+HttpUtility.UrlDecode(f));int s=r.IndexOf(
" = ")+3;return r.Substring(s,r.IndexOf("<",f)-s);}}
答案 2 :(得分:10)
:[[/%^(:[[+-/^,&i|:[$[' ']^j+0__:k<3:]]
答案 3 :(得分:9)
好的,我实现了monadic解析器组合库,然后使用该库来解决这个问题。所有人都告诉它仍然只有70行可读代码。
我假设取幂关联到右边,其他操作符关联到左边。一切都适用于浮动(System.Doubles)。我没有对错误输入或除零进行任何错误处理。
// Core Parser Library
open System
let Fail() = fun i -> None
type ParseMonad() =
member p.Return x = fun i -> Some(x,i)
member p.Bind(m,f) = fun i ->
match m i with
| Some(x,i2) -> f x i2
| None -> None
let parse = ParseMonad()
let (<|>) p1 p2 = fun i ->
match p1 i with
| Some r -> Some(r)
| None -> p2 i
let Sat pred = fun i ->
match i with
| [] -> None
| c::cs -> if pred c then Some(c, cs) else None
// Auxiliary Parser Library
let Digit = Sat Char.IsDigit
let Lit (c : char) r =
parse { let! _ = Sat ((=) c)
return r }
let Opt p = p <|> parse { return [] }
let rec Many p = Opt (Many1 p)
and Many1 p = parse { let! x = p
let! xs = Many p
return x :: xs }
let Num = parse {
let! sign = Opt(Lit '-' ['-'])
let! beforeDec = Many Digit
let! rest = parse { let! dec = Lit '.' '.'
let! afterDec = Many Digit
return dec :: afterDec } |> Opt
let s = new string(List.concat([sign;beforeDec;rest])
|> List.to_array)
match(try Some(float s) with e -> None)with
| Some(r) -> return r
| None -> return! Fail() }
let Chainl1 p op =
let rec Help x = parse { let! f = op
let! y = p
return! Help (f x y) }
<|> parse { return x }
parse { let! x = p
return! Help x }
let rec Chainr1 p op =
parse { let! x = p
return! parse { let! f = op
let! y = Chainr1 p op
return f x y }
<|> parse { return x } }
// Expression grammar of this code-golf question
let AddOp = Lit '+' (fun x y -> 0. + x + y)
<|> Lit '-' (fun x y -> 0. + x - y)
let MulOp = Lit '*' (fun x y -> 0. + x * y)
<|> Lit '/' (fun x y -> 0. + x / y)
let ExpOp = Lit '^' (fun x y -> Math.Pow(x,y))
let rec Expr = Chainl1 Term AddOp
and Term = Chainl1 Factor MulOp
and Factor = Chainr1 Part ExpOp
and Part = Num <|> Paren
and Paren = parse { do! Lit '(' ()
let! e = Expr
do! Lit ')' ()
return e }
let CodeGolf (s:string) =
match Expr(Seq.to_list(s.ToCharArray())) with
| None -> "bad input"
| Some(r,_) -> r.ToString()
// Examples
printfn "%s" (CodeGolf "1.1+2.2+10^2^3") // 100000003.3
printfn "%s" (CodeGolf "10+3.14/2") // 11.57
printfn "%s" (CodeGolf "(10+3.14)/2") // 6.57
printfn "%s" (CodeGolf "-1^(-3*4/-6)") // 1
printfn "%s" (CodeGolf "-2^(2^(4-1))") // 256
printfn "%s" (CodeGolf "2*6/4^2*4/3") // 1
解析器表示类型是
type P<'a> = char list -> option<'a * char list>
顺便说一句,这是非错误处理解析器的常用方法。
答案 4 :(得分:8)
PARLANSE中的递归下降解析器,一种具有LISP语法的类C语言: [70行,1376个字符不计算SO所需的缩进量4] 编辑:规则改变了,有人坚持浮点数,修复。 没有库调用,浮动转换,输入和打印除外。 [现在有94行,1825个字符]
(define main (procedure void)
(local
(;; (define f (function float void))
(= [s string] (append (input) "$"))
(= [i natural] 1)
(define S (lambda f
(let (= v (P))
(value (loop
(case s:i)
"+" (;; (+= i) (+= v (P) );;
"-" (;; (+= i) (-= v (P) );;
else (return v)
)case
)loop
v
)value
)define
(define P (lambda f
(let (= v (T))
(value (loop
(case s:i)
"*" (;; (+= i) (= v (* v (T)) );;
"/" (;; (+= i) (= v (/ v (T)) );;
else (return v)
)case
)loop
v
)value
)define
(define T (lambda f
(let (= v (O))
(value (loop
(case s:i)
"^" (;; (+= i) (= v (** v (T)) );;
else (return v)
)case
)loop
v
)value
)define
(define O (lambda f
(let (= v +0)
(value
(case s:i)
"(" (;; (+= i) (= v (E)) (+= i) );;
"-" (;; (+= i) (= v (- 0.0 (O))) );;
else (= v (StringToFloat (F))
)value
v
)let
)define
(define F (lambda f)
(let (= n (N))
(value
(;; (ifthen (== s:i ".")
(;; (+= i)
(= n (append n "."))
(= n (concatenate n (N)))
);;
)ifthen
(ifthen (== s:i "E")
(;; (+= i)
(= n (append n "E"))
(ifthen (== s:i "-")
(;; (+= i)
(= n (append n "-"))
(= n (concatenate n (N)))
);;
);;
)ifthen
);;
n
)let
)define
(define N (lambda (function string string)
(case s:i
(any "0" "1" "2" "3" "4" "5" "6" "7" "8" "9")
(value (+= i)
(append ? s:(-- i))
)value
else ?
)case
)define
);;
(print (S))
)local
)define
假设一个格式良好的表达式,至少浮动数字 一个前导数字,指数可选为Enn或E-nnn。 没有编译和运行。
Pecularities:定义f本质上是签名typedef。 lambdas是解析函数,每个语法规则一个。 通过写(F args)调用函数F. PARLANSE函数是词法范围的,因此每个函数都有 隐式访问要评估的表达式和 字符串扫描索引i。
实施的语法是:
E = S $ ;
S = P ;
S = S + P ;
P = T ;
P = P * T ;
T = O ;
T = O ^ T ;
O = ( S ) ;
O = - O ;
O = F ;
F = digits {. digits} { E {-} digits} ;
答案 5 :(得分:5)
我将先前的解决方案高尔夫压缩到这个宝石中:
let rec D a=function|c::s when System.Char.IsDigit c->D(c::a)s|s->a,s
and L p o s=
let rec K(a,s)=match o s with|None->a,s|Some(o,t)->let q,t=p t in K(o a q,t)
K(p s)
and E=L(L F (function|'*'::s->Some((*),s)|'/'::s->Some((/),s)|_->None))(
function|'+'::s->Some((+),s)|'-'::s->Some((-),s)|_->None)
and F s=match P s with|x,'^'::s->let y,s=F s in x**y,s|r->r
and P=function|'('::s->let r,_::s=E s in r,s|s->(
let a,s=match(match s with|'-'::t->D['-']t|_->D[]s)with|a,'.'::t->D('.'::a)t|r->r
float(new string(Seq.to_array(List.rev a))),s)
and G s=string(fst(E(Seq.to_list s)))
试验:
printfn "%s" (G "1.1+2.2+10^2^3") // 100000003.3
printfn "%s" (G "10+3.14/2") // 11.57
printfn "%s" (G "(10+3.14)/2") // 6.57
printfn "%s" (G "-1^(-3*4/-6)") // 1
printfn "%s" (G "-2^(2^(4-1))") // 256
printfn "%s" (G "2*6/4^2*4/3") // 1
printfn "%s" (G "3-2-1") // 0
答案 6 :(得分:4)
好的,我实现了monadic解析器组合库,然后使用该库来解决这个问题。所有人都告诉它大约150行代码。 (这基本上是我的F#解决方案的直接音译。)
我假设取幂关联到右边,其他操作符关联到左边。一切都适用于System.Doubles。我没有对错误输入或除零进行任何错误处理。
using System;
using System.Collections.Generic;
using System.Linq;
class Option<T>
{
public T Value { get; set; }
public Option(T x) { Value = x; }
}
delegate Option<KeyValuePair<T,List<char>>> P<T>(List<char> input);
static class Program
{
static List<T> Cons<T>(T x, List<T> xs)
{
var r = new List<T>(xs);
r.Insert(0, x);
return r;
}
static Option<T> Some<T>(T x) { return new Option<T>(x); }
static KeyValuePair<T,List<char>> KVP<T>(T x, List<char> y)
{ return new KeyValuePair<T,List<char>>(x,y); }
// Core Parser Library
static P<T> Fail<T>() { return i => null; }
static P<U> Select<T, U>(this P<T> p, Func<T, U> f)
{
return i =>
{
var r = p(i);
if (r == null) return null;
return Some(KVP(f(r.Value.Key),(r.Value.Value)));
};
}
public static P<V> SelectMany<T, U, V>(this P<T> p, Func<T, P<U>> sel, Func<T, U, V> prj)
{
return i =>
{
var r = p(i);
if (r == null) return null;
var p2 = sel(r.Value.Key);
var r2 = p2(r.Value.Value);
if (r2 == null) return null;
return Some(KVP(prj(r.Value.Key, r2.Value.Key),(r2.Value.Value)));
};
}
static P<T> Or<T>(this P<T> p1, P<T> p2)
{
return i =>
{
var r = p1(i);
if (r == null) return p2(i);
return r;
};
}
static P<char> Sat(Func<char,bool> pred)
{
return i =>
{
if (i.Count == 0 || !pred(i[0])) return null;
var rest = new List<char>(i);
rest.RemoveAt(0);
return Some(KVP(i[0], rest));
};
}
static P<T> Return<T>(T x)
{
return i => Some(KVP(x,i));
}
// Auxiliary Parser Library
static P<char> Digit = Sat(Char.IsDigit);
static P<T> Lit<T>(char c, T r)
{
return from dummy in Sat(x => x == c)
select r;
}
static P<List<T>> Opt<T>(P<List<T>> p)
{
return p.Or(Return(new List<T>()));
}
static P<List<T>> Many<T>(P<T> p)
{
return Many1<T>(p).Or(Return(new List<T>()));
}
static P<List<T>> Many1<T>(P<T> p)
{
return from x in p
from xs in Many(p)
select Cons(x, xs);
}
static P<T> Chainl1<T>(this P<T> p, P<Func<T, T, T>> op)
{
return from x in p
from r in Chainl1Helper(x, p, op)
select r;
}
static P<T> Chainl1Helper<T>(T x, P<T> p, P<Func<T, T, T>> op)
{
return (from f in op
from y in p
from r in Chainl1Helper(f(x, y), p, op)
select r)
.Or(Return(x));
}
static P<T> Chainr1<T>(this P<T> p, P<Func<T, T, T>> op)
{
return (from x in p
from r in (from f in op
from y in Chainr1(p, op)
select f(x, y))
.Or(Return(x))
select r);
}
static P<double> TryParse(string s)
{
double d;
if (Double.TryParse(s, out d)) return Return(d);
return Fail<double>();
}
static void Main(string[] args)
{
var Num = from sign in Opt(Lit('-', new List<char>(new []{'-'})))
from beforeDec in Many(Digit)
from rest in Opt(from dec in Lit('.','.')
from afterDec in Many(Digit)
select Cons(dec, afterDec))
let s = new string(Enumerable.Concat(sign,
Enumerable.Concat(beforeDec, rest))
.ToArray())
from r in TryParse(s)
select r;
// Expression grammar of this code-golf question
var AddOp = Lit('+', new Func<double,double,double>((x,y) => x + y))
.Or(Lit('-', new Func<double, double, double>((x, y) => x - y)));
var MulOp = Lit('*', new Func<double, double, double>((x, y) => x * y))
.Or(Lit('/', new Func<double, double, double>((x, y) => x / y)));
var ExpOp = Lit('^', new Func<double, double, double>((x, y) => Math.Pow(x, y)));
P<double> Expr = null;
P<double> Term = null;
P<double> Factor = null;
P<double> Part = null;
P<double> Paren = from _1 in Lit('(', 0)
from e in Expr
from _2 in Lit(')', 0)
select e;
Part = Num.Or(Paren);
Factor = Chainr1(Part, ExpOp);
Term = Chainl1(Factor, MulOp);
Expr = Chainl1(Term, AddOp);
Func<string,string> CodeGolf = s =>
Expr(new List<char>(s)).Value.Key.ToString();
// Examples
Console.WriteLine(CodeGolf("1.1+2.2+10^2^3")); // 100000003.3
Console.WriteLine(CodeGolf("10+3.14/2")); // 11.57
Console.WriteLine(CodeGolf("(10+3.14)/2")); // 6.57
Console.WriteLine(CodeGolf("-1^(-3*4/-6)")); // 1
Console.WriteLine(CodeGolf("-2^(2^(4-1))")); // 256
Console.WriteLine(CodeGolf("2*6/4^2*4/3")); // 1
}
}
答案 7 :(得分:2)
#define V(c)D o;for(**s-40?*r=strtod(*s,s):(++*s,M(s,r)),o=**s?strchr(t,*(*s)++)-t:0;c;)L(r,&o,s);
typedef char*S;typedef double D;D strtod(),pow();S*t=")+-*/^",strchr();
L(D*v,D*p,S*s){D u,*r=&u;V(*p<o)*v=*p-1?*p-2?*p-3?*p-4?pow(*v,u):*v/u:
*v*u:*v-u:*v+u;*p=o;}M(S*s,D*r){V(o)}
第一个换行是必需的,我把它算作一个字符。
这是与my other answer完全不同的方法。它更像是一种功能性方法。这个代码不是标记化和循环多次,而是使用递归调用高优先级运算符来一次性计算表达式,有效地使用调用堆栈来存储状态。
为了满足strager ;),这次我添加了strtod()
,pow()
和strchr()
的前向声明。取出它们可以节省26个字符。
入口点为 M()
。输入字符串是第一个参数,输出double是第二个参数。入口点曾经是E()
,它返回一个字符串,如OP所要求的那样。但由于我是唯一一个这样做的C实现,我决定把它拉出来(同伴压力,等等)。重新添加它将添加43个字符:
E(S s,S r){D v;M(&s,&v);sprintf(r,"%g",v);}
以下是压缩之前的代码:
double strtod(),pow(),Solve();
int OpOrder(char op){
int i=-1;
while("\0)+-*/^"[++i] != op);
return i/2;
}
double GetValue(char **s){
if(**s == '('){
++*s;
return Solve(s);
}
return strtod(*s, s);
}
double Calculate(double left, char *op, char **s){
double right;
char rightOp;
if(*op == 0 || *op == ')')
return left;
right = GetValue(s);
rightOp = *(*s)++;
while(OpOrder(*op) < OpOrder(rightOp))
right = Calculate(right, &rightOp, s);
switch(*op){
case '+': left += right; break;
case '-': left -= right; break;
case '*': left *= right; break;
case '/': left /= right; break;
case '^': left = pow(left, right); break;
}
*op = rightOp;
return left;
}
double Solve(char **s){
double value = GetValue(s);
char op = *(*s)++;
while(op != 0 && op != ')')
value = Calculate(value, &op, s);
return value;
}
void Evaluate(char *expression, char *result){
sprintf(result, "%g", Solve(&expression));
}
由于OP的“reference implementation”在C#中,我也写了一个半压缩的C#版本:
D P(D o){
return o!=6?o!=7&&o!=2?o<2?0:1:2:3;
}
D T(ref S s){
int i;
if(s[i=0]<48)
i++;
while(i<s.Length&&s[i]>47&s[i]<58|s[i]==46)
i++;
S t=s;
s=s.Substring(i);
return D.Parse(t.Substring(0,i));
}
D V(ref S s,out D o){
D r;
if(s[0]!=40)
r=T(ref s);
else{s=s.Substring(1);r=M(ref s);}
if(s=="")
o=0;
else{o=s[0]&7;s=s.Substring(1);}
return r;
}
void L(ref D v,ref D o,ref S s){
D p,r=V(ref s,out p),u=v;
for(;P(o)<P(p);)
L(ref r,ref p,ref s);
v = new Func<D>[]{()=>u*r,()=>u+r,()=>0,()=>u-r,()=>Math.Pow(u,r),()=>u/r}[(int)o-2]();
o=p;
}
D M(ref S s){
for(D o,r=V(ref s,out o);o>1)
L(ref r,ref o,ref s);
return r;
}
答案 8 :(得分:2)
#include<stdio.h>
#include<string.h>
#include<math.h>
float X(char*c){struct{float f;int d,c;}N[99],*C,*E,*P;char*o="+-*/^()",*q,d=1,x
=0;for(C=N;*c;){C->f=C->c=0;if(q=strchr(o,*c)){if(*c<42)d+=*c-41?8:-8;else{if(C
==N|C[-1].c)goto F;C->d=d+(q-o)/2*2;C->c=q-o+1;++C;}++c;}else{int n=0;F:sscanf(c
,"%f%n",&C->f,&n);c+=n;C->d=d;++C;}}for(E=N;E-C;++E)x=E->d>x?E->d:x;for(;x>0;--x
)for(E=P=N;E-C;E->d&&!E->c?P=E:0,++E)if(E->d==x&&E->c){switch((E++)->c){
#define Z(x,n)case n:P->f=P->f x E->f;break;
Z(+,1)Z(-,2)Z(*,3)Z(/,4)case 5:P->f=powf(P->f,E->f);}E->d=0;}return N->f;}
#include<stdio.h>
#include<string.h>
#include<math.h>
float X(char*c){
struct{
float f;
int d,c;
}N[99],*C,*E,*P;
char*o="+-*/^()",*q,d=1,x=0;
for(C=N;*c;){
C->f=C->c=0;
if(q=strchr(o,*c)){
if(*c<42) // Parentheses.
d+=*c-41?8:-8;
else{ // +-*/^.
if(C==N|C[-1].c)
goto F;
C->d=d+(q-o)/2*2;
C->c=q-o+1;
++C;
}
++c;
}else{
int n=0;
F:
sscanf(c,"%f%n",&C->f,&n);
c+=n;
C->d=d;
++C;
}
}
for(E=N;E-C;++E)
x=E->d>x?E->d:x;
for(;x>0;--x)
for(E=P=N;E-C;E->d&&!E->c?P=E:0,++E)
if(E->d==x&&E->c){
switch((E++)->c){
#define Z(x,n)case n:P->f=P->f x E->f;break;
Z(+,1)
Z(-,2)
Z(*,3)
Z(/,4)
case 5:
P->f=powf(P->f,E->f);
}
E->d=0;
}
return N->f;
}
int main(){
assert(X("2+2")==4);
assert(X("-1^(-3*4/-6)")==1);
assert(X("-2^(2^(4-1))")==256);
assert(X("2*6/4^2*4/3")==1);
puts("success");
}
开发了我自己的技术。自己弄清楚。 =]
答案 9 :(得分:1)
我知道,我知道......这个代码 - 高尔夫似乎已经关闭了。 不过,我觉得有必要在erlang __ 中编写这些东西,所以这里是一个erlang版本(没有找到高尔夫格式的意愿,所以这些是58行,大约1400个字符)
-module (math_eval).
-export ([eval/1]).
eval( Str ) ->
ev(number, Str,[]).
ev( _, [], Stack ) -> [Num] = do(Stack), Num;
ev( State, [$ |Str], Stack ) ->
ev( State,Str,Stack );
ev( number, [$(|Str], Stack ) ->
ev( number,Str,[$(|Stack] );
ev( number, Str, Stack ) ->
{Num,Str1} = r(Str),
ev( operator,Str1,[Num|Stack] );
ev( operator, [$)|Str], Stack) ->
ev( operator, Str, do(Stack) );
ev( operator, [Op2|Str], [N2,Op,N1|T]=Stack ) when is_float(N1) andalso is_float(N2) ->
case p(Op2,Op) of
true -> ev( number, Str, [Op2|Stack]);
false -> ev( operator, [Op2|Str], [c(Op,N1,N2)|T] )
end;
ev( operator, [Op|Str], Stack ) ->
ev( number,Str,[Op|Stack] ).
do(Stack) ->
do(Stack,0).
do([],V) -> [V];
do([$(|Stack],V) -> [V|Stack];
do([N2,Op,N1|Stack],0) ->
do(Stack,c(Op,N1,N2));
do([Op,N1|Stack],V) ->
do(Stack,c(Op,N1,V)).
p(O1,O2) -> op(O1) < op(O2).
op(O) ->
case O of
$) -> 0; $( -> 0;
$^ -> 1;
$* -> 2; $/ -> 2;
$+ -> 3; $- -> 3;
$ -> 4; _ -> -1
end.
r(L) ->
r(L,[]).
r([], Out) ->
{f( lists:reverse(Out) ),[]};
r([$-|R],[]) ->
r(R,[$-]);
r([C|T]=R,O) ->
if (C =< $9 andalso C >= $0) orelse C =:= $. -> r(T,[C|O]);
true -> {f(lists:reverse(O)),R}
end.
f(L) ->
case lists:any(fun(C) -> C =:= $. end,L) of
true -> list_to_float(L);
false -> list_to_float(L++".0")
end.
c($+,A,B) -> A+B;
c($-,A,B) -> A-B;
c($*,A,B) -> A*B;
c($/,A,B) -> A/B;
c($^,A,B) -> math:pow(A,B).
答案 10 :(得分:1)
char*c;double m(char*s,int o){int i;c=s;double x=*s-40?strtod(c,&s):m(c+1,0);double y;for(;*c&&c-41;c++){for(i=0;i<7&&*c-"``-+/*^"[i];i++);if(i<7){if(i/2<=o/2){c-=*c!=41;break;}y=m(c+1,i);x=i-6?i-5?i-4?i-3?i-2?x:x-y:x+y:x/y:x*y:pow(x,y);}}return x;}
这是我以前版本的一个有点改版的版本。通过使用strtod
代替atof
(道具给P爸爸),我能够减少~90个字符!
将指针传递给以空字符结尾的字符数组,然后是0.就像这样:
m(charPtr,0)
您必须在调用函数的源文件中包含math.h和stdlib.h。另请注意,char * c是在代码开头全局定义的。所以不要在使用它的任何东西中定义任何名为c的变量。如果你必须有办法否定事物,“ - [在这里插入表达式]”相当于“(0- [在此处插入表达式])”OP优先顺序的方式
答案 11 :(得分:1)
这些可能会挤满了很多。 C程序包括不严格需要的头文件和一些其他条目不包含的main()程序。 Ruby程序执行I / O来获取字符串,这在技术上并不是必需的......
我意识到递归下降解析器并不真正需要为每个优先级单独的例程,即使它总是在引用中显示。所以我通过将三个二进制优先级级别折叠成一个带有优先级参数的递归例程来修改我之前的Ruby条目。我添加了C89以获得乐趣。有趣的是,这两个程序的行数大致相同。
puts class RHEvaluator
def setup e
@opByPri, @x, @TOPPRI = [[?+,0],[?-,0],[?*,1],[?/,1],[?^,2]], e, 3
getsym
rhEval 0
end
def getsym
@c = @x[0]
@x = @x.drop 1
end
def flatEval(op, a, b)
case op
when ?* then a*b
when ?/ then a/b
when ?+ then a+b
when ?- then a-b
when ?^ then a**b
end
end
def factor
t = @c
getsym
t = case t
when ?- then -factor
when ?0..?9 then t.to_f - ?0
when ?(
t = rhEval 0
getsym # eat )
t
end
t
end
def rhEval pri
return factor if pri >= @TOPPRI;
v = rhEval pri + 1
while (q = @opByPri.assoc(@c)) && q[1] == pri
op = @c
getsym
v = flatEval op, v, rhEval(pri + 1)
end
v
end
RHEvaluator # return an expression from the class def
end.new.setup gets.bytes.to_a
#include <stdio.h>
#include <math.h>
#include <strings.h>
#define TOPPRI '3'
#define getsym() token = *x++;
const char opByPri[] = "+0-0*1/1^2";
char token, *x;
double rhEval(int);
int main(int ac, char **av) {
x = av[1];
getsym();
return printf("%f\n", rhEval('0')), 0;
}
double flatEval(char op, double a, double b) {
switch (op) {
case '*': return a * b;
case '/': return a / b;
case '+': return a + b;
case '-': return a - b;
case '^': return pow(a, b);
} }
double factor(void) {
double d; char t = token;
getsym();
switch (t) {
case '-': return -factor();
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
return t - '0';
case '(': d = rhEval('0');
getsym();
}
return d;
}
double rhEval(int pri) {
double v; char *q;
if (pri >= TOPPRI)
return factor();
v = rhEval(pri + 1);
while ((q = index(opByPri, token)) && q[1] == pri) {
char op = token;
getsym();
v = flatEval(op, v, rhEval(pri + 1));
}
return v;
}
答案 12 :(得分:1)
(625包括格式如下以避免水平滚动,如果我使其可读,则为42行。)
double x(char*e,int*p);
D(char c){return c>=48&&c<=57;}
S(char c){return c==43||c==45;}
double h(char*e,int*p){double r=0,s=1,f=0,m=1;int P=*p;if(e[P]==40){
P++;r=x(e,&P);P++;}else if(D(e[P])||S(e[P])){s=S(e[P])?44-e[P++]:s;
while(D(e[P]))r=r*10+e[P++]-48;if(e[P]==46)while(D(e[++P])){f=f*10+e[P]-48;
m*=10;}r=s*(r+f/m);}*p=P;return r;}
double x(char*e,int*p){double r=0,t,d,x,s=1;do{char o=42;t=1;do{d=h(e,p);
while(e[*p]==94){(*p)++;x=h(e,p);d=pow(d,x);}t=o==42?t*d:t/d;o=e[*p];
if(o==42||o==47)(*p)++;else o=0;}while(o);r+=s*t;s=S(e[*p])?44-e[(*p)++]:0;
}while(s);return r;}
double X(char*e){int p=0;return x(e,&p);}
这是我的第一个代码高尔夫。
我自己正在解析浮点数,我使用的唯一库函数是pow
。
我通过多次提升纠正了错误,并对括号进行了处理。我还创建了主函数X()
,只需要一个字符串作为参数。但它仍然会返回一个双倍。
<强>扩展强>
42个非空行
double x(char*e, int*p);
D(char c) { return c>=48 && c<=57; }
S(char c) { return c==43 || c==45; }
double h(char*e, int*p) {
double r=0, s=1, f=0, m=1;
int P=*p;
if(e[P]==40) {
P++;
r=x(e, &P);
P++; }
else if(D(e[P]) || S(e[P])) {
s=S(e[P]) ? 44-e[P++] : s;
while(D(e[P]))
r=r*10+e[P++]-48;
if(e[P]==46)
while(D(e[++P])) {
f=f*10+e[P]-48;
m*=10; }
r=s*(r+f/m); }
*p=P;
return r; }
double x(char*e, int*p) {
double r=0, t, d, x, s=1;
do {
char o=42;
t=1;
do {
d=h(e, p);
while(e[*p]==94) {
(*p)++;
x=h(e, p);
d=pow(d, x); }
t=o==42 ? t*d : t/d;
o=e[*p];
if(o==42 || o==47) (*p)++;
else o=0;
} while(o);
r+=s*t;
s=S(e[*p]) ? 44-e[(*p)++] : 0;
} while(s);
return r; }
double X(char*e) {int p=0; return x(e, &p);}
答案 13 :(得分:1)
这个主要是避开普遍性,只关注编写一个递归下降解析器来解决这个问题。
open System
let rec Digits acc = function
| c::cs when Char.IsDigit(c) -> Digits (c::acc) cs
| rest -> acc,rest
let Num = function
| cs ->
let acc,cs = match cs with|'-'::t->['-'],t |_->[],cs
let acc,cs = Digits acc cs
let acc,cs = match cs with
| '.'::t -> Digits ('.'::acc) t
| _ -> acc, cs
let s = new string(List.rev acc |> List.to_array)
float s, cs
let Chainl p op cs =
let mutable r, cs = p cs
let mutable finished = false
while not finished do
match op cs with
| None -> finished <- true
| Some(op, cs2) ->
let r2, cs2 = p cs2
r <- op r r2
cs <- cs2
r, cs
let rec Chainr p op cs =
let x, cs = p cs
match op cs with
| None -> x, cs
| Some(f, cs) -> // TODO not tail-recursive
let y, cs = Chainr p op cs
f x y, cs
let AddOp = function
| '+'::cs -> Some((fun x y -> 0. + x + y), cs)
| '-'::cs -> Some((fun x y -> 0. + x - y), cs)
| _ -> None
let MulOp = function
| '*'::cs -> Some((fun x y -> 0. + x * y), cs)
| '/'::cs -> Some((fun x y -> 0. + x / y), cs)
| _ -> None
let ExpOp = function
| '^'::cs -> Some((fun x y -> Math.Pow(x,y)), cs)
| _ -> None
let rec Expr = Chainl Term AddOp
and Term = Chainl Factor MulOp
and Factor = Chainr Part ExpOp
and Part = function
| '('::cs -> let r, cs = Expr cs
if List.hd cs <> ')' then failwith "boom"
r, List.tl cs
| cs -> Num cs
let CodeGolf (s:string) =
Seq.to_list s |> Expr |> fst |> string
// Examples
printfn "%s" (CodeGolf "1.1+2.2+10^2^3") // 100000003.3
printfn "%s" (CodeGolf "10+3.14/2") // 11.57
printfn "%s" (CodeGolf "(10+3.14)/2") // 6.57
printfn "%s" (CodeGolf "-1^(-3*4/-6)") // 1
printfn "%s" (CodeGolf "-2^(2^(4-1))") // 256
printfn "%s" (CodeGolf "2*6/4^2*4/3") // 1
printfn "%s" (CodeGolf "3-2-1") // 0
答案 14 :(得分:0)
我在http://www.sumtree.com写了一个attp作为教师的教育工具。
使用bison进行解析,使用wxwidgets进行GUI。
答案 15 :(得分:0)
puts class RHEvaluator
def setup e
@x = e
getsym
rhEval
end
def getsym
@c = @x[0]
@x = @x.drop 1
end
def flatEval(op, a, b)
case op
when ?* then a*b
when ?/ then a/b
when ?+ then a+b
when ?- then a-b
when ?^ then a**b
end
end
def factor
t = @c
getsym
t = case t
when ?- then -factor
when ?0..?9 then t.to_f - ?0
when ?(
t = rhEval
getsym # eat )
t
end
t
end
def power
v = factor
while @c == ?^
op = @c
getsym
v = flatEval op, v, factor
end
v
end
def multiplier
v = power
while @c == ?* or @c == ?/
op = @c
getsym
v = flatEval op, v, power
end
v
end
def rhEval
v = multiplier
while @c == ?+ or @c == ?-
op = @c
getsym
v = flatEval op, v, multiplier
end
v
end
RHEvaluator # return an expression from the class def
end.new.setup gets.bytes.to_a
答案 16 :(得分:0)
我的第一次尝试。这是一个带控制台IO的完整程序。
using System;using System.Collections.Generic;using System.Linq;
using F3 = System.Func<double, double, double>;using C = System.Char;using D = System.Double;
using I = System.Int32;using S = System.String;using W = System.Action;
class F{public static void Main(){Console.WriteLine(new F().EE(Console.ReadLine()));}
D EE(S s){s="("+s.Replace(" ","")+")";
return V(LT(s.Select((c,i)=>c!='-'||P(s[i-1])<0||s[i-1]==')'?c:'_')).GroupBy(t=>t.Item2).Select(g=>new S(g.Select(t=>t.Item1).ToArray())));}
I P(C c){return (" __^^*/+-()".IndexOf(c)-1)/2;}
D V(IEnumerable<S> s){Func<S,C,I>I=(_,c)=>_.IndexOf(c);
I l=0,n=0;var U=new List<S>();var E=new Stack<D>();var O=new Stack<C>();
Func<D>X=E.Pop;Action<D>Y=E.Push;F3 rpow=(x,y)=>Math.Pow(y,x);F3 rdiv=(x,y)=>y/x;
W[]OA={()=>Y(rpow(X(),X())),()=>Y(X()*X()),()=>Y(rdiv(X(),X())),()=>Y(X()+X()),()=>Y(-X()+X()),()=>Y(-X()),};
O.Push(')');foreach(S k in s.TakeWhile(t=>l>0||n==0)){n++;I a=I("(",k[0])-I(")",k[0]);l+=a;
if(l>1||l==-a)U.Add(k);else{if(U.Count>0)E.Push(V(U));U.Clear();I p = Math.Min(P(k[0]),P('-'));
if(p<0)E.Push(D.Parse(k));else{while(P(O.Peek())<=p)OA[I("^*/+-_",O.Pop())]();O.Push(k[0]);}}}
return X();}
IEnumerable<Tuple<C,I>> LT(IEnumerable<C> s){I i=-1,l=-2;foreach(C c in s){I p=P(c);if(p>=0||p!=l)i++;l=P(c);yield return Tuple.Create(c,i);}}}
这里没有高尔夫球化:
using System;
using System.Collections.Generic;
using System.Linq;
class E
{
public static void Main()
{
Console.WriteLine(EvalEntry(Console.ReadLine()));
}
public static double EvalEntry(string s)
{
return Eval(Tokenize("(" + s.Replace(" ", "") + ")"));
}
const char UnaryMinus = '_';
static int Precedence(char op)
{
// __ and () have special (illogical at first glance) placement as an "optimization" aka hack
return (" __^^*/+-()".IndexOf(op) - 1) / 2;
}
static double Eval(IEnumerable<string> s)
{
Func<string, char, int> I = (_, c) => _.IndexOf(c);
Func<char, int> L = c => I("(", c) - I(")", c);
// level
int l = 0;
// token count
int n = 0;
// subeval
var U = new List<string>();
// evaluation stack
var E = new Stack<double>();
// operation stack
var O = new Stack<char>();
Func<double> pop = E.Pop;
Action<double> push = E.Push;
Func<double, double, double> rpow = (x, y) => Math.Pow(y, x);
Func<double, double, double> rdiv = (x, y) => y / x;
// ^*/+-_
Action[] operationActions =
{
() => push(rpow(pop(), pop())),
() => push(pop()*pop()),
() => push(rdiv(pop(),pop())),
() => push(pop()+pop()),
() => push(-pop()+pop()),
() => push(-pop()),
};
Func<char, Action> getAction = c => operationActions["^*/+-_".IndexOf(c)];
// ohhhhh here we have another hack!
O.Push(')');
foreach (var k in s.TakeWhile(t => l > 0 || n == 0))
{
n++;
int adjust = L(k[0]);
l += L(k[0]);
/* major abuse of input conditioning here to catch the ')' of a subgroup
* (level == 1 && adjust == -1) => (level == -adjust)
*/
if (l > 1 || l == -adjust)
{
U.Add(k);
continue;
}
if (U.Count > 0)
{
E.Push(Eval(U));
U.Clear();
}
int prec = Math.Min(Precedence(k[0]), Precedence('-'));
// just push the number if it's a number
if (prec == -1)
{
E.Push(double.Parse(k));
}
else
{
while (Precedence(O.Peek()) <= prec)
{
// apply op
getAction(O.Pop())();
}
O.Push(k[0]);
}
}
return E.Pop();
}
static IEnumerable<string> Tokenize(string s)
{
return
LocateTokens(PreprocessUnary(s))
.GroupBy(t => t.Item2)
.Select(g => new string(g.Select(t => t.Item1).ToArray()));
}
// make sure the string doesn't start with -
static IEnumerable<char> PreprocessUnary(string s)
{
return s.Select((c, i) => c != '-' || Precedence(s[i - 1]) < 0 || s[i - 1] == ')' ? c : UnaryMinus);
}
static IEnumerable<Tuple<char, int>> LocateTokens(IEnumerable<char> chars)
{
int i = -1;
int lastPrec = -2;
foreach (char c in chars)
{
var prec = Precedence(c);
if (prec >= 0 || prec != lastPrec)
{
i++;
lastPrec = Precedence(c);
}
yield return Tuple.Create(c, i);
}
}
}
答案 17 :(得分:0)
这是我在C#中的“参考实现”(有点笨拙)。
static int RevIndexOf(string S, char Ch, int StartPos)
{
for (int P = StartPos; P >= 0; P--)
if (S[P] == Ch)
return P;
return -1;
}
static bool IsDigit(char Ch)
{
return (((Ch >= '0') && (Ch <= '9')) || (Ch == '.'));
}
static int GetNextOperator(List<string> Tokens)
{
int R = Tokens.IndexOf("^");
if (R != -1)
return R;
int P1 = Tokens.IndexOf("*");
int P2 = Tokens.IndexOf("/");
if ((P1 == -1) && (P2 != -1))
return P2;
if ((P1 != -1) && (P2 == -1))
return P1;
if ((P1 != -1) && (P2 != -1))
return Math.Min(P1, P2);
P1 = Tokens.IndexOf("+");
P2 = Tokens.IndexOf("-");
if ((P1 == -1) && (P2 != -1))
return P2;
if ((P1 != -1) && (P2 == -1))
return P1;
if ((P1 != -1) && (P2 != -1))
return Math.Min(P1, P2);
return -1;
}
static string ParseSubExpression(string SubExpression)
{
string[] AA = new string[] { "--", "++", "+-", "-+" };
string[] BB = new string[] { "+", "+", "-", "-" };
for (int I = 0; I < 4; I++)
while (SubExpression.IndexOf(AA[I]) != -1)
SubExpression = SubExpression.Replace(AA[I], BB[I]);
const string Operators = "^*/+-";
List<string> Tokens = new List<string>();
string Token = "";
foreach (char Ch in SubExpression)
if (IsDigit(Ch) || (("+-".IndexOf(Ch) != -1) && (Token == "")))
Token += Ch;
else
if (Operators.IndexOf(Ch) != -1)
{
Tokens.Add(Token);
Tokens.Add(Ch + "");
Token = "";
}
else
throw new Exception("Unhandled error: invalid expression.");
Tokens.Add(Token);
int P1 = GetNextOperator(Tokens);
while (P1 != -1)
{
double A = double.Parse(Tokens[P1 - 1]);
double B = double.Parse(Tokens[P1 + 1]);
double R = 0;
switch (Tokens[P1][0])
{
case '^':
R = Math.Pow(A, B);
break;
case '*':
R = A * B;
break;
case '/':
R = A / B;
break;
case '+':
R = A + B;
break;
case '-':
R = A - B;
break;
}
Tokens[P1] = R.ToString();
Tokens.RemoveAt(P1 + 1);
Tokens.RemoveAt(P1 - 1);
P1 = GetNextOperator(Tokens);
}
if (Tokens.Count == 1)
return Tokens[0];
else
throw new Exception("Unhandled error.");
}
static bool FindSubExpression(string Expression, out string Left, out string Middle, out string Right)
{
int P2 = Expression.IndexOf(')');
if (P2 == -1)
{
Left = "";
Middle = "";
Right = "";
return false;
}
else
{
int P1 = RevIndexOf(Expression, '(', P2);
if (P1 == -1)
throw new Exception("Unhandled error: unbalanced parentheses.");
Left = Expression.Substring(0, P1);
Middle = Expression.Substring(P1 + 1, P2 - P1 - 1);
Right = Expression.Remove(0, P2 + 1);
return true;
}
}
static string ParseExpression(string Expression)
{
Expression = Expression.Replace(" ", "");
string Left, Middle, Right;
while (FindSubExpression(Expression, out Left, out Middle, out Right))
Expression = Left + ParseSubExpression(Middle) + Right;
return ParseSubExpression(Expression);
}