Đánh giá một chuỗi các biểu thức toán học đơn giản [đóng]


76

Thử thách

Đây là thách thức (về phát minh của riêng tôi, mặc dù tôi sẽ không ngạc nhiên nếu trước đó nó đã xuất hiện ở những nơi khác trên web).

Viết một hàm nhận một đối số là biểu diễn chuỗi của một biểu thức toán học đơn giản và đánh giá nó như một giá trị dấu phẩy động. "Biểu thức đơn giản" có thể bao gồm bất kỳ giá trị nào sau đây: số thập phân dương hoặc âm, + , - , * , / , ( , ) . Biểu thức sử dụng ký hiệu infix (bình thường) . Người vận hành phải được đánh giá theo thứ tự xuất hiện, tức là không phải như trong BODMAS , mặc dù tất nhiên các dấu ngoặc phải được quan sát chính xác. Hàm sẽ trả về kết quả chính xác cho bất kỳbiểu hiện có thể có của hình thức này. Tuy nhiên, hàm không phải xử lý các biểu thức không đúng định dạng (tức là những biểu thức có cú pháp sai).

Ví dụ về các biểu thức:

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

Quy tắc

Tôi dự đoán một số hình thức "gian lận" / xảo quyệt ở đây, vì vậy xin vui lòng cho tôi biết trước để chống lại nó! Bằng cách gian lận, tôi đề cập đến việc sử dụng hàm evalhoặc hàm tương đương trong các ngôn ngữ động như JavaScript hoặc PHP, hoặc biên dịch và thực thi mã một cách nhanh chóng. (Tuy nhiên, tôi nghĩ rằng đặc điểm kỹ thuật "không có BODMAS" của tôi đã đảm bảo khá nhiều điều này.) Ngoài ra, không có hạn chế nào. Tôi dự đoán một vài giải pháp Regex ở đây, nhưng sẽ rất tuyệt nếu thấy nhiều hơn thế.

Bây giờ, tôi chủ yếu quan tâm đến giải pháp C # /. NET ở đây, nhưng bất kỳ ngôn ngữ nào khác cũng hoàn toàn có thể chấp nhận được (cụ thể là F # và Python cho các phương pháp tiếp cận chức năng / hỗn hợp). Tôi vẫn chưa quyết định xem mình sẽ chấp nhận giải pháp ngắn gọn hay khéo léo nhất (ít nhất là đối với ngôn ngữ) làm câu trả lời, nhưng tôi hoan nghênh mọi hình thức giải pháp bằng bất kỳ ngôn ngữ nào , ngoại trừ những gì tôi vừa cấm ở trên !

Giải pháp của tôi

Bây giờ tôi đã đăng giải pháp C # của mình ở đây (403 ký tự). Cập nhật: Giải pháp mới của tôi đã đánh bại đáng kể giải pháp cũ với 294 ký tự , với sự trợ giúp của một chút regex đáng yêu! Tôi nghi ngờ rằng điều này sẽ dễ dàng bị đánh bại bởi một số ngôn ngữ có cú pháp nhẹ hơn (đặc biệt là những ngôn ngữ vui nhộn / động) và đã được chứng minh là đúng, nhưng tôi rất tò mò liệu ai đó có thể đánh bại điều này trong C # hay không.

Cập nhật

Tôi đã thấy một số giải pháp rất xảo quyệt. Cảm ơn tất cả những người đã đăng một. Mặc dù tôi chưa thử nghiệm bất kỳ cái nào trong số chúng, nhưng tôi sẽ tin tưởng mọi người và cho rằng ít nhất họ cũng làm việc với tất cả các ví dụ đã cho.

Đối với lưu ý, re-entrancy (tức là an toàn luồng) không phải là một yêu cầu đối với chức năng, mặc dù nó là một phần thưởng.


định dạng

Vui lòng đăng tất cả các câu trả lời ở định dạng sau để dễ so sánh:

Ngôn ngữ

Số ký tự: ???

Chức năng bị xáo trộn hoàn toàn:

(code here)

Chức năng rõ ràng / bán khó hiểu:

(code here)

Mọi ghi chú về thuật toán / phím tắt thông minh cần có.



2
Có thể ý bạn là ví dụ đầu tiên của bạn bằng 0,125 (di chuyển vị trí thập phân) và ví dụ thứ hai bằng 99 ở phía bên trái (một quá nhiều số chín).
John Y

Vâng, cảm ơn. Đó là những lỗi chính tả khá rõ ràng.
Noldorin

1
Bạn nên thêm một ví dụ trong đó việc thiếu BODMAS là đáng kể, ví dụ: "1 + 1 * 3 = 6"
Ben Blank,

3
Ahh, tôi đang tự hỏi khi nào thì cuộc bỏ phiếu đầu tiên kết thúc. Lưu ý với tất cả những người bình chọn: đã có rất nhiều câu hỏi chơi gôn mã mở trong StackOverflow. Sự đồng thuận dường như vẫn ổn - chủ yếu chỉ là một chút vui vẻ.
Noldorin

3
Tôi có xu hướng đồng ý rằng điều này là tốt, đặc biệt là "wiki"
Marc Gravell

Câu trả lời:


36

Perl (không có đánh giá)

Số ký tự: 167 106 (xem bên dưới để biết phiên bản 106 ký tự)

Hàm hoàn toàn xáo trộn: (167 ký tự nếu bạn nối ba dòng này thành một)

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

Phiên bản rõ ràng / giải mã:

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

Tôi đã đọc sai các quy tắc ban đầu, vì vậy tôi đã gửi một phiên bản có "eval". Đây là một phiên bản không có nó.

Bit mới nhất của cái nhìn sâu sắc đến khi tôi nhận ra rằng chữ số bát phân cuối cùng trong mã ký tự cho +, -, /, và *là khác nhau, và đó ord(undef)là 0. Điều này cho phép tôi thiết lập bảng công văn @anhư một mảng, và chỉ cần gọi mã ở vị trí 7 & ord($3).

Có một điểm rõ ràng là cạo bỏ một ký tự nữa - thay đổi q""thành ''- nhưng điều đó sẽ khiến việc cắt và dán vào vỏ khó hơn.

Thậm chí ngắn hơn

Số ký tự: 124 106

Lấy chỉnh sửa bởi ephemient vào tài khoản, bây giờ xuống còn 124 ký tự: (tham gia hai dòng vào một)

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

Ngắn hơn vẫn

Số ký tự: 110 106

Giải pháp ruby ​​bên dưới đang đẩy tôi đi xa hơn, mặc dù tôi không thể đạt được 104 ký tự của nó:

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

Tôi đã phải nhượng bộ và sử dụng ''. sendThủ thuật ruby đó thực sự hữu ích cho vấn đề này.

Vắt nước từ đá

Số ký tự: 106

Một tỷ lệ nhỏ để tránh kiểm tra chia cho không.

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

Đây là khai thác thử nghiệm cho chức năng này:

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
Thật là đáng sợ khi Perl nhỏ có thể đi, tôi đã chỉnh sửa câu trả lời của mình để giữ cho nó là cài đặt Ruby nhỏ nhất và hết chỗ ở 170 ký tự. Nhưng 124? Nước thịt ngon!
Robert K

1
Tôi không nhận thấy rằng chưa ai đề cập đến nó, nhưng giải pháp này yêu cầu Perl 5.10. Để tương thích với 5.8, hãy sử dụng (-? (?> \ D + (\. \ D +)?)) Dài hơn hai ký tự.
ephemient

7
@Epaga, đừng lo lắng, tôi đã hiểu lỗi chính tả của bạn: perl. Là. tuyệt vời.
Danny

1
Hãy rút ngắn nó đi 1 ký tự - thay đổi "$ _ = $ _ [0]" thành "($ _) = @ _".
Chris Lutz

1
Bởi vì nó thực hiện số học một cách vô điều kiện mà không phụ thuộc vào toán tử (chọn kết quả chính xác sau đó), nó cần tránh chia cho số không.
ephemient

44

Người lắp ráp

427 byte

Làm xáo trộn, được lắp ráp với A86 tuyệt vời thành một tệp thực thi .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

CHỈNH SỬA: Nguồn không nhầm lẫn:

        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
Assembly - đây là lập trình thực sự !
Andreas Rejbrand

Tôi đã từng thấy một trò chơi Tetris đầy đủ trong 64 byte
BlueRaja - Danny Pflughoeft

29

Ruby

Số ký tự: 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

Đây là phiên bản không đệ quy của giải pháp The Wicked Flea. Biểu thức phụ trong ngoặc đơn được đánh giá từ dưới lên thay vì từ trên xuống.

Chỉnh sửa : Việc chuyển đổi 'while' thành đệ quy có điều kiện + đuôi đã lưu một vài ký tự, vì vậy nó không còn là không đệ quy nữa (mặc dù đệ quy không cần thiết về mặt ngữ nghĩa.)

Chỉnh sửa : Mượn ý tưởng của Daniel Martin về việc hợp nhất các regexps tiết kiệm được 11 nhân vật khác!

Chỉnh sửa : Đệ quy đó thậm chí còn hữu ích hơn tôi nghĩ đầu tiên! x.to_fcó thể được viết lại e(x), nếu xtình cờ chứa một số duy nhất.

Chỉnh sửa : Sử dụng ' or' thay vì ' ||' cho phép bỏ một cặp dấu ngoặc đơn.

Phiên bản dài:

# 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

Tôi gần như nghĩ rằng đây là thủ lĩnh mới cho đến khi tôi nhìn thấy Perl đã được chỉnh sửa để trở nên ngắn hơn nữa! Làm tốt lắm, dù sao.
Noldorin

1
Loại bỏ 'e = readline.chomp; ...; p e.to_f' và sử dụng 'def q (e); ...; e.to_f; end' như các giải pháp khác sẽ tiết kiệm được 10 ký tự. Tuy nhiên, nó không thành q ("1 + 3 / -8") == - 0,5 như trong câu hỏi.
ephemient

@ephemient đó là một lỗi bạn đã tìm thấy - nó không thể xử lý các số âm.
finnw

1
Gsub! ('-', '') trong mã của tôi là để chỉ cách hoạt động của đối số trong ngoặc đơn, nếu bị phủ định. Nếu kết quả bên trong của dấu ngoặc đơn bị phủ định là âm thì dấu trừ bên ngoài câu lệnh vẫn là: --7.0, chẳng hạn. Tuy nhiên, hỗ trợ đó khiến tôi tiêu tốn 24 ký tự, vẫn còn 19 ký tự trên bạn. Tôi không biết rằng tôi có thể thu nhỏ nó hơn bất kỳ thủ thuật nào tôi học được từ bạn. (Nhưng tôi đã làm tuyệt vời cho một thử 2!)
Robert K

1
Sử dụng "send" thực sự sắp vi phạm quy tắc "no eval". Nhưng mẹo hay là kết hợp các khoảng trắng vào số regex của bạn. Sử dụng thủ thuật đó và một thủ thuật khác, giải pháp perl của tôi giảm xuống còn 119 ký tự.
Daniel Martin

29

C (VS2005)

Số lượng nhân vật: 1360

Lạm dụng bộ tiền xử lý và cảnh báo cho bố cục mã vui nhộn (cuộn xuống để xem):

#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

Số ký tự: 9759

Bản thân tôi giống một vận động viên ném bóng hơn.

LƯU Ý: không tính đến các dấu ngoặc đơn lồng nhau. Ngoài ra, chưa được kiểm tra, nhưng tôi khá chắc chắn rằng nó hoạt động.

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
Tôi cũng có thể đã đạt được 10k ký tự nếu trời không quá khuya :)
Jason

Bạn có biết ít ký tự hơn là tốt hơn? Bằng cách này, họ không bao giờ nghĩ vb.net là tốt.
Ikke

11
@ikke - nó được cho là càng ít ký tự càng tốt? trời ơi ... có vẻ ai đó đã bỏ sót điểm
Jason

3
ZERO_STRING_REPRESENTATION trông giống như một cái gì đó thuộc về thedailywtf
erikkallen

1
+1 điều này khiến tôi cười nhiều hơn bất kỳ câu trả lời nào khác trên SO. đã từng .
R .. GitHub NGỪNG TRỢ GIÚP

25

Haskell

Số ký tự: 182

Không cần cố gắng thông minh, chỉ cần một số nén: 4 dòng, 312 byte.

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

Và bây giờ, thực sự hòa vào tinh thần chơi gôn, 3 dòng và 182 byte:

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

Đã nổ:

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

Tôi nghi ngờ rằng vẫn có thể xuống dưới mức 225, nhưng điều này là xa nhất mà tôi có thể đạt được vào lúc 3 giờ sáng.
ephemient

Đó dường như là một giải pháp chức năng khá thanh lịch. (Một điều mà tôi chắc chắn sẽ không thể hiểu được nếu không có các nhận xét, vì vậy cảm ơn vì những điều đó.) Ngoài ra, bạn chỉ đang dẫn đầu giải pháp Python của Dave vào lúc này, vì vậy bạn dường như đang dẫn đầu! Tôi rất tò mò nếu một giải pháp F # có thể phù hợp hoặc thậm chí đánh bại điều này.
Noldorin

Tôi thật thú vị khi giải pháp Parsec (tổ hợp phân tích cú pháp = regex tổng quát + hơn thế nữa), ngay cả khi đã cố gắng tối thiểu hóa, vẫn không đến gần với phân tích cú pháp cuộn bằng tay. Tôi không nghĩ rằng cú pháp của F # có thể ngắn gọn như Haskell, nhưng tôi cũng hoan nghênh một số cạnh tranh :)
ephemient

@ephemient: Tôi đã thử sử dụng nhanh cả regex và mô-đun phân tích cú pháp trong Python. Gần như ngay lập tức dưới 300 ký tự, nhưng không có cơ hội cạnh tranh. Vấn đề là việc nhập và các lệnh gọi hàm ăn quá nhiều. Điều đó đúng với hầu hết các ngôn ngữ (ngoại lệ Perl). BTW, bạn không cần phải tự phân tích cú pháp để nhận được dưới 300 ký tự về cơ bản, như giải pháp của tôi cho thấy.
stephan

2
Tôi nghĩ rằng bạn đã quá phạt số nhân vật của mình. Bài toán yêu cầu hàm String-> Double, vì vậy bạn nên đếm các ký tự bằng cách thay thế "main = tương tác $ show." với "q =", cho 17 ký tự khác, đặt số lượng của bạn là 209.
Daniel Martin

23

Python

Số ký tự: 237

Chức năng bị xáo trộn hoàn toàn:

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

Chức năng rõ ràng / bán khó hiểu:

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

Lưu ý: phiên bản hiện tại không xử lý đầu vào như - (1.0) mặc dù nó xử lý các ký tự phủ định một cách chính xác. Không rõ ràng từ thông số kỹ thuật nếu điều này là bắt buộc.
Dave

Người ta có thể làm cho l không toàn cầu miễn phí bằng cách đưa nó vào danh sách tham số của e. Tuy nhiên, vẫn sẽ không an toàn cho chuỗi.
Dave

Tinh ranh lắm. Điều đó rất đáng để nỗ lực thông dịch. :)
Nick Johnson

@Dave: Của tôi cũng không thành công -(1.0), vì vậy đừng lo lắng! Tôi sẽ làm rõ câu hỏi. Dù sao, có vẻ như giải pháp rất thông minh - tôi vẫn đang cố gắng tìm ra cách nó hoạt động (không biết chính xác Python). Nếu bạn có thể thêm một lời giải thích ngắn gọn, điều đó sẽ được đánh giá cao.
Noldorin

20

Fortran 77 (phương ngữ gfortran, hiện có hỗ trợ g77)

Số ký tự: 2059

Phiên bản bị xáo trộn:

      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

Phiên bản rõ ràng: (3340 ký tự với đoạn đầu phim)

      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

Ghi chú Phiên bản đã chỉnh sửa này khá ác hơn nỗ lực đầu tiên của tôi. Cùng một thuật toán, nhưng bây giờ nội dòng với một mớ khủng khiếp của gotos. Tôi đã từ bỏ các quy trình chung, nhưng hiện đang sử dụng một vài hương vị của các nhánh được tính toán. Tất cả kiểm tra và báo cáo lỗi đã bị xóa, nhưng phiên bản này sẽ âm thầm khôi phục từ một số lớp ký tự không mong muốn trong đầu vào. Phiên bản này cũng biên dịch với g77.

Các giới hạn chính vẫn là định dạng cứng nhắc của fortran, các từ khóa dài và phổ biến cũng như các từ khóa sơ khai đơn giản.


14
Chúa ơi! Hôm nay chắc hẳn bạn đã cảm thấy buồn chán. ;)
gnovice,

2
Hehe, tôi không nghĩ rằng tôi đã từng mong đợi một giải pháp Fortran! Tôi nghĩ rằng chúng ta có thể kết luận rằng ngôn ngữ này không đặc biệt phù hợp để viết mã golf? Dù sao cũng được bình chọn vì nỗ lực tuyệt đối và sử dụng một ngôn ngữ cổ. :)
Noldorin

Tôi thấy loại byte khó xử lý này là dài dòng và vụng về trong fortran, nhưng không thực sự khó. Mặt khác, viết mã không có cấu trúc và sử dụng các nhánh được tính toán đó, cảm thấy hơi kỳ quặc.
dmckee --- ex-moderator kitten

Tuyệt vời, nhưng làm thế nào để phiên bản fortran hơn 2000 nhân vật nhận được nhiều phiếu bầu hơn phiên bản ruby1.9 nhỏ bé của tôi? lol
Daniel Huckstep

@darkhelmet: Tôi không biết. Tôi đã làm điều đó trên một con chim sơn ca và mong đợi một hoặc hai phiếu bầu cho nỗ lực và sự nghịch ngợm. Tôi tự hào một cách tục tĩu về sự ghê tởm này, nhưng điều này thật nực cười ...
dmckee --- chú mèo con cựu điều hành viên

17

C99

Số ký tự: 239 (Nhưng xem bên dưới để biết 209 )

chức năng nén:

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

chức năng giải nén:

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

Chức năng không được đăng nhập lại.

CHỈNH SỬA từ Chris Lutz : Tôi ghét giẫm đạp lên mã của người khác, nhưng đây là 209 phiên bản ký tự :

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

Có thể đọc được (tốt, không thực sự dễ đọc, nhưng được giải nén):

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()là một macro, không phải là một chức năng, nhưng nó hoạt động. Phiên bản có thể đọc được có một số logic được viết lại nhưng không được sắp xếp lại (như o != '+'thay vì o - '+'), mà chỉ là phiên bản thụt lề (và được xử lý trước) của phiên bản khác. Tôi tiếp tục cố gắng đơn giản hóa if(!o|o==41)return a;phần vào for()vòng lặp, nhưng nó không bao giờ làm cho nó ngắn hơn. Tôi vẫn tin rằng nó có thể được thực hiện, nhưng tôi đã chơi golf xong. Nếu tôi làm việc với câu hỏi này nữa, nó sẽ ở ngôn ngữ không được đặt tên .


Giải pháp tuyệt vời và điểm thưởng khi sử dụng C. Beats "nguyên chất" của tôi bằng 3 ký tự! Re-entrancy không nằm trong quy tắc, vì vậy điều đó tốt. (Tuy nhiên, đó là một điểm cộng.)
Noldorin

Đẹp! Bạn có thể bỏ bớt một vài ký tự bằng cách sử dụng mã ASCII, ví dụ: thay thế '0' bằng 48, v.v. Và tất nhiên, bạn có thể tiết kiệm một loạt ký tự bằng cách sử dụng atof () thay vì trình phân tích cú pháp float tự phát triển của mình, nhưng bạn cố ý không sử dụng các hàm thư viện, đó không phải là yêu cầu chặt chẽ của bài toán.
Adam Rosenfield

Tôi đã nghĩ đến việc sử dụng atof () nhưng nó không cho bạn biết chuỗi float kết thúc ở đâu nên bạn vẫn cần phải phân tích cú pháp nó.
Ferruccio

Cảm ơn vì mẹo, Adam. Sử dụng điều đó và một vài thủ thuật (xấu xí) khác, tôi thu nhỏ nó hơn một chút.
Ferruccio

Rất tiếc, tôi không tính đến số âm. Mã tăng lên 400 ký tự.
Ferruccio

13

Lisp chung

(SBCL)
Số ký tự: 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")"))))

Phiên bản thích hợp (387 ký tự):

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

Đầu vào là biểu mẫu w(), nhận một đối số chuỗi. Nó sử dụng thủ thuật rằng nums / toán hạng và toán tử nằm trong mẫu KHÔNG CÓ ... và đánh giá đệ quy tất cả các toán hạng, và do đó việc lồng vào nhau rất rẻ. ;)


Giải pháp thông minh. Tuy nhiên, tôi không chắc nó hoàn toàn hợp lệ vì đặc tả dành cho hàm nhận một đối tượng chuỗi.
Noldorin

Xin lỗi vì điều đó. Đã sửa!

Không vấn đề gì. Không nhận ra rằng việc chuyển đổi quá dễ dàng. Giải pháp tốt, vẫn còn!
Noldorin

Chà. Đó là vẻ đẹp. :)
Emil H

11

JavaScript (Không tương thích với IE)

Số ký tự: 268/260

Chức năng bị xáo trộn hoàn toàn:

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

hoặc, trong JavaScript 1.8 (Firefox 3+), bạn có thể lưu một vài ký tự bằng cách sử dụng các đóng biểu thức:

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

Chức năng rõ ràng / bán khó hiểu:

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

Không phiên bản nào sẽ hoạt động trong IE, bởi vì chúng sử dụng chỉ số kiểu mảng trên chuỗi. Nếu bạn thay thế cả hai lần xuất hiện x[0]bằngx.charAt(0) , thì đầu tiên sẽ hoạt động ở mọi nơi.

Tôi đã cắt bỏ một số ký tự khác kể từ phiên bản đầu tiên bằng cách chuyển các biến thành tham số hàm và thay thế một câu lệnh if khác bằng toán tử điều kiện.


Đó là khá tốt. Tôi đã đợi ai đó sử dụng regex ở đây. :) Có vẻ như các ngôn ngữ động chắc chắn có lợi thế hơn cho vấn đề này.
Noldorin

8

C # với Regex Love

Số ký tự: 384

Hoàn toàn bị xáo trộn:

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

Không bị xáo trộn:

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

Tận dụng tính năng nhóm cân bằng Regex của .NET .


Cảm ơn vì giải pháp đó. :) Tôi không chắc liệu mình có thấy giải pháp C # với regex hay không, nhưng ở đây chúng tôi có nó. Bây giờ, có thể tranh luận rằng bạn có nên bao gồm "using System.Text.RegularExpressions;" trong số lượng char của bạn, nhưng đó là một giải pháp tốt.
Noldorin

Đó không phải là một phần của quy tắc :). Nếu bạn thêm "using R = System.Text.RegularExpressions.Regex;" và thay thế "Regex" của tôi bằng R, nó chuyển thành 417.
Jeff Moser vào

@Jeff: Về mặt kỹ thuật, nó sẽ không biên dịch mà không có câu lệnh using, vì vậy theo mặc định, nó nên được đưa vào. Tuy nhiên, điểm đáng quan tâm là các giải pháp C # của chúng tôi đều đi sau đáng kể.
Noldorin

8

PHP

Số ký tự: 284

lộn xộn:

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

có thể đọc được:

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)

Nên hoạt động với bất kỳ đầu vào hợp lệ nào (bao gồm số âm và khoảng trắng tùy ý)


preg_replace()với công cụ esửa đổi sẽ giúp bạn tiết kiệm thêm một số byte.
Alix Axel

8

SQL (SQL Server 2008)

Số ký tự: 4202

Chức năng bị xáo trộn hoàn toàn:

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

Chức năng rõ ràng / bán khó hiểu:

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

Nó không phải là ngắn nhất. Nhưng tôi nghĩ rằng nó rất linh hoạt cho SQL. Thật dễ dàng để thêm các toán tử mới. Thật dễ dàng để thay đổi mức độ ưu tiên của các toán tử.


1
Chúa ơi, tôi không nghĩ mình đã từng mong đợi một giải pháp SQL! Điều này không hoàn toàn theo tinh thần của trò chơi gôn mã, nhưng dù sao cũng được bầu chọn vì sự táo bạo (và thậm chí không sử dụng ngôn ngữ lập trình). :)
Noldorin

2
@Noldorin, tại sao nó không theo tinh thần của mã golf?
tuinstoel 25/09/09

6

F #

Số ký tự: 327

OP đang tìm kiếm một phiên bản F #, nó đây. Có thể được thực hiện một đẹp hơn rất nhiều kể từ khi tôi đang lợi dụng một ref đây để tiết kiệm ký tự. Nó xử lý hầu hết các thứ như - (1.0) , 3 - -3 và thậm chí 0 - .5, v.v.

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)

Thật vậy, tôi đã hy vọng một giải pháp F #. Cảm ơn vì điều đó. Số lượng Char cũng khá tốt, đặc biệt khi xem xét rằng "System.Text.RegularExpressions.Regex.Matches" chiếm một số lượng ký tự vô lý.
Noldorin

yeah, tương tự với lệnh gọi .Value.IsEmpty / Tail / Head - Tôi đã có một phiên bản mới trong quá trình thực hiện; p hy vọng cho các ký tự phụ 250.
Thr

Tôi thực sự không chắc liệu trong một số cuộc thi chơi gôn mã, bạn có được phép nhập / sử dụng các câu lệnh ngoài số ký tự hay không. Điều đó chắc chắn sẽ hữu ích, nếu vậy. :) Rất mong được xem phiên bản mới.
Noldorin

@Noldorin: Không, tôi xin lỗi vì tôi không thể lấy nó dưới 327 ký tự của mã này (được nâng cao một chút kể từ lần trước). Lợi ích từ việc mọi thứ được phân tích cú pháp hoàn hảo với regex lớn hơn cái tên dài ngoằng của "System.Text.RegularExpressions.Regex.Matches" Nếu F # sẽ có một tên ngắn (bí danh) cho hàm Matches, tôi sẽ ở 288 ký tự, nhưng nó không = /.
thr

@fredrikholmstrom: Đừng lo lắng - dù sao thì giải pháp tốt. Ngoài ra, tôi không hoàn toàn chắc chắn, nhưng tôi sẽ nói rằng bạn sẽ có thể chuyển "System.Text.RegularExpressions" thành một câu lệnh "mở" và loại trừ số lượng char cho điều đó ít nhất.
Noldorin

6

J

Số ký tự: 208

Sau bình luận của Jeff Moser , tôi nhận ra rằng tôi đã hoàn toàn quên mất ngôn ngữ này ... Tôi không phải là chuyên gia, nhưng nỗ lực đầu tiên của tôi đã diễn ra khá suôn sẻ.

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
)

Nó hơi khó chịu, phải lập bản đồ x/y-zvào của J x%y_z . Nếu không có điều đó, có thể 50% mã này có thể biến mất.


Vâng, điều đó khá hay. Bây giờ những gì về một giải pháp trong K? : P Tôi đang nghi ngờ rằng thậm chí có thể đánh bại Perl.
Noldorin

Tuyệt vời, tôi đã có được giải pháp Haskell theo giải pháp J của mình! Mặc dù nếu ai đó ở đây là thuật sĩ J hoặc K hoặc APL, họ có thể sẽ phá hủy rào cản 200 ký tự ...
ephemient

6

Python (không cần nhập bất kỳ thứ gì)

Số ký tự: 222

Tôi đã đánh cắp nhiều thủ thuật từ câu trả lời của Dave, nhưng tôi đã cố gắng loại bỏ thêm một số ký tự.

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

Phiên bản nhận xét:

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 Ý tưởng chính tả hay. Tuy nhiên, phiên bản hiện tại không thành công trên e ('1 + 0'). Thay vào đó hãy sử dụng {'+': n + m, '-': nm, ' ': n m, '/': n / m nếu m khác 1}. Tôi đã mượn ý tưởng của bạn (với sửa đổi này). Cảm ơn
stephan

Cảm ơn. Tôi đã không nghĩ đến vấn đề DivZero; sửa 7 ký tự là n / (m hoặc 1).
krubo

Cũng sẽ làm điều này cho chương trình của tôi ;-)
stephan

hehe, không thay đổi gì bây giờ, số lượng nhân vật đẹp :)
Tetha

5

C #

Số ký tự: 403

Vì vậy, đây là giải pháp của tôi ... Tôi vẫn đang chờ ai đó đăng một trong C # có thể đánh bại nó. (Marc Gravell đã thành công, và có thể còn làm tốt hơn tôi sau một số lần mày mò.)

Chức năng bị xáo trộn hoàn toàn:

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

Hàm nửa hỗn tạp:

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ó vẻ như không có gì quá thông minh xảy ra ở đây. Tuy nhiên, chức năng này có lợi thế là có thể tham gia lại (tức là an toàn luồng).

Tôi cũng hài lòng một cách hợp lý với số lượng ký tự, vì nó được viết bằng C # (tôi tin là hợp lệ 1.0, 2.0 và 3.0).


Mọi lời khuyên về cách tôi có thể giảm số lượng char hơn nữa sẽ được hoan nghênh. (Đây là nỗ lực thực sự đầu tiên của tôi đối với môn chơi gôn mã.)
Noldorin vào

Tôi đã nhận được nó <400, nhưng nó không thành công trong bài kiểm tra đã chỉnh sửa mà bạn đã thêm ;-p
Marc Gravell

Gợi ý: "var" cho float, char - chỉ cạo một số và mất khả năng tương thích với C # 1.2 / 2.0.
Marc Gravell

@Marc: Vâng, đó là khoảng thời gian tôi hiểu. Với một vài thay đổi nhỏ khác, tôi có thể giảm nó xuống 390, nhưng không kém.
Noldorin

Giải pháp tốt đẹp Nolorin. Tôi đã có thể đưa giải pháp của bạn xuống 361
Chris Persichetti

5

Đây là một cái khác:

Tập lệnh shell (sử dụng sed + awk)

Số ký tự: 295

lộn xộn:

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

đọc được

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

Kiểm tra:

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

Kết quả:

2.68344117647058823526
2.68344

Tôi hầu như không biết điều này hoạt động như thế nào, nhưng tôi rất ngạc nhiên về cách một shell script thực hiện tốt nhiệm vụ này! Thực sự tốt.
Noldorin

Vâng, chỉ cần nhớ rằng nhiều nhiều hệ điều hành sử dụng ngôn ngữ / công cụ kết hợp nhiều nhiều nhiệm vụ khác nhau :)
soulmerge

5

MATLAB (v7.8.0)

Số ký tự: 239

Hàm bị xáo trộn:

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

Chức năng Clear (er):

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

Kiểm tra:

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

Tóm tắt nội dung: Một hỗn hợp của biểu thức chính quy và đệ quy. Khá nhiều điều tốt nhất tôi có thể làm cho đến nay, không gian lận và sử dụng EVAL.


5

Ruby

Số ký tự: 170

Làm xáo trộn:

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

Đọc được:

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

Không có gì bối rối thực sự cho cái này, mà tôi quyết định đăng mới vì nó rất khác so với lần đầu tiên của tôi. Tôi lẽ ra phải thấy điều này ngay từ đầu. Quá trình này là một quá trình loại bỏ rất đơn giản: tìm và giải quyết cặp dấu ngoặc đơn cao nhất (lồng nhau nhất) thành một số cho đến khi không tìm thấy nữa, sau đó giải quyết tất cả các số hiện có và các phép toán thành kết quả. Và, trong khi giải quyết các câu lệnh trong ngoặc đơn, tôi có nó loại bỏ tất cả các dấu gạch ngang (Float.to_f không biết phải làm gì với chúng).

Vì vậy, nó hỗ trợ các số dương và âm (+3, 3, & -3) và thậm chí cả các biểu thức phụ bị phủ định trong ngoặc đơn chỉ theo thứ tự xử lý. Cách triển khai ngắn hơn duy nhất là Perl (w / o eval).

Chỉnh sửa: Tôi vẫn đang theo đuổi Perl, nhưng đây là câu trả lời nhỏ thứ hai ngay bây giờ. Tôi thu nhỏ nó bằng các thay đổi đối với regex thứ hai và bằng cách thay đổi cách xử lý chuỗi là phá hủy (thay thế chuỗi cũ). Điều này loại bỏ nhu cầu sao chép chuỗi, mà tôi phát hiện ra chỉ là một con trỏ mới đến chuỗi. Và đổi tên hàm thành s từ giải quyết đã lưu một vài ký tự.


Rất tốt, thật ngạc nhiên khi tôi không tự mình thử cách tiếp cận này, vì tôi đã sử dụng một thứ rất giống để giải quyết một câu hỏi phân tích cú pháp khác.
Mike Tunnicliffe

Xem giải pháp của tôi để biết cách nén regexp đó. Bạn cũng không cần 'dải' cuối cùng. Và có vẻ như bạn không hoàn toàn triển khai trừ một bậc, vì vậy bạn nhận được ít lợi ích từ gsub ('-', '').
finnw

Tôi thực sự không thể rút ngắn thuật toán cụ thể của mình hoặc tôi thất bại 3-4 trong số các bài kiểm tra, tôi không chắc tại sao. Tuy nhiên, tôi có thể thu nhỏ nó khoảng 20 ký tự.
Robert K

4

Python với các biểu thức chính quy

Số ký tự: 283

Chức năng bị xáo trộn hoàn toàn:

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]

Không bị xáo trộn:

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]

Tôi muốn xem liệu tôi có đánh bại các giải pháp Python khác bằng cách sử dụng biểu thức chính quy hay không.

Không thể.

Biểu thức chính quy mà tôi đang sử dụng tạo danh sách các cặp (val, op) trong đó chỉ một mục trong mỗi cặp là hợp lệ. Phần còn lại của mã là một trình phân tích cú pháp dựa trên ngăn xếp khá chuẩn với một mẹo nhỏ là thay thế 3 ô trên cùng trong ngăn xếp bằng kết quả tính toán bằng cú pháp gán danh sách Python. Làm cho điều này hoạt động với số âm chỉ yêu cầu hai ký tự bổ sung (-? Trong regex).


Bạn có thể tiết kiệm một vài byte bằng cách xóa "()" khỏi chuỗi toán tử của bạn; zipdừng ở cuối danh sách ngắn hơn.
Ben Blank

@gooli: Bạn có đang sử dụng Windows không? Theo số lượng của tôi, giải pháp đã đăng chỉ là 273. Một giải thích cho điều này có thể là bạn tính các dòng mới là hai ký tự mỗi dòng. (Python không quan tâm nếu bạn có newlines đơn char, ngay cả trong Windows.) Giải thích khác là nhấn 8 khi bạn có nghĩa là 7.;)
John Y

4

Python

Số ký tự: 382

Tuy nhiên, một giải pháp Python khác, sử dụng nhiều thay thế biểu thức chính quy. Mỗi lần chạy qua vòng lặp, các biểu thức đơn giản nhất được tính toán và kết quả được đưa trở lại chuỗi.

Đây là mã không bị xáo trộn, trừ khi bạn coi các biểu thức chính quy là bị xáo trộn.

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

Có ý tưởng này ngay khi tôi đang tìm hiểu và không thể để nó đi cho đến khi tôi viết ra và thực hiện nó.


1
Giải pháp tốt ... Tôi cũng thấy rất rõ ràng. Có vẻ như việc sử dụng dict / zip để lưu trữ các toán tử chắc chắn là một cách tiếp cận rất hiệu quả trong Python.
Noldorin

4

C #

Số ký tự: 396 (đã cập nhật)

(nhưng không thành công trong bài kiểm tra bạn đã thêm với "/ -8" và tôi không muốn sửa nó ...

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

Từ:

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

Ah tuyệt vời, một giải pháp C #. Việc bạn sử dụng các kiểu nullable nói riêng khá thú vị. 484 có vẻ khá tốt, vì bạn không có thời gian để dọn dẹp nó. (Tôi tin rằng một cải tiến là chuyển đổi câu lệnh switch thành một chuỗi ifs.) Tôi đã đăng giải pháp C # của riêng mình ngay bây giờ, nếu bạn muốn so sánh. :)
Noldorin

4

Python

Số ký tự: 235

Chức năng bị xáo trộn hoàn toàn:

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

Bán lộn xộn:

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, giải pháp Python thứ n + 1. Trong một sự lạm dụng trắng trợn của thử ngoại trừ, tôi sử dụng phương pháp thử-và-sai. Nó sẽ xử lý tất cả các trường hợp đúng cách bao gồm những thứ như thế -(8), --8g('-(1 - 3)'). Nó là tái tham gia. Không hỗ trợ cho --trường hợp mà nhiều triển khai không hỗ trợ, nó ở mức 217 ký tự (xem bản sửa đổi trước).

Cảm ơn vì một giờ thú vị vào Chủ nhật và 30 phút nữa vào Thứ Hai. Cảm ơn krubo vì câu đọc hay của anh ấy.


Một cách tiếp cận thú vị khác ... Có độ dài giống hệt với một trong các giải pháp Python khác. Điều này khẳng định quan điểm của tôi rằng việc sử dụng từ điển các toán tử là cách nên đi, nếu có thể. Tôi muốn làm điều gì đó tương tự trong C #, nhưng cú pháp chỉ đơn giản là chiếm quá nhiều ký tự.
Noldorin

4

Ruby

Số lượng ký tự: 217 179

Đây là giải pháp ruby ​​ngắn nhất cho đến nay (một giải pháp dựa nhiều vào RegExp đưa ra câu trả lời không chính xác khi chuỗi chứa ít nhóm dấu ngoặc đơn)-- không còn đúng nữa. Các giải pháp dựa trên regex và thay thế ngắn hơn. Cái này dựa trên chồng tích lũy và phân tích cú pháp toàn bộ biểu thức từ trái sang phải. Nó là người tham gia lại và không sửa đổi chuỗi đầu vào. Nó có thể bị buộc tội vi phạm các quy tắc không sử dụng eval, vì nó gọi Floatcác phương thức có tên giống hệt như thuật ngữ toán học của chúng (+, -, /, *).

Mã bị xáo trộn (phiên bản cũ, được chỉnh sửa bên dưới) :

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

Mã khó hiểu hơn:

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

Mã sạch:

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

Trên thực tế, của tôi ngắn hơn (198) và sử dụng regex để giải quyết dấu ngoặc từ trên xuống trước kết quả cuối cùng của toán học. Vì vậy, "3 + (3 * (3 + 9))" là: "3 + (3 * 12)", "3 + 36", 39. Nó đi từ trên xuống dưới, từ trái sang phải. Nó giải quyết tất cả các bài kiểm tra, ngoại trừ một sự giám sát nhỏ yêu cầu khoảng cách giữa các mã thông báo. Xem: stackoverflow.com/questions/928563/…
Robert K

Không phải là của bạn không thông minh, mà là rất nhiều.
Robert K

(3 + 7) - (5 + 2) - đó là ý của tôi đối với một số nhóm dấu ngoặc đơn. Giải pháp của bạn có vấn đề với các dấu ngoặc đơn không lồng nhau, do tính tham lam của regex.
samuil

Có thể là vậy, nhưng tôi đã mày mò với trình phân tích cú pháp của mình vào đêm hôm qua và cải thiện nó trên hệ thống của tôi (với các hàm toán học và các biến chữ cái). Vì vậy, tôi đã lấy regex tốt hơn của mình từ nó và nó hoạt động tốt, bài đăng cũng được cập nhật với số lượng char mới. ;-) Tôi sẽ đưa phương trình của bạn vào các bài kiểm tra trong câu trả lời trong giây lát.
Robert K

Tôi không nghĩ việc sử dụng 'method' hoặc 'send' là gian lận - đó chỉ là tra cứu bảng - bạn không sử dụng trình phân tích cú pháp tích hợp sẵn.
finnw

3

Ruby 1.8.7

Số ký tự: 620

Hãy thử và thực hiện dễ dàng, đây là lần đầu tiên tôi viết một trình phân tích cú pháp biểu thức trong đời! Tôi đảm bảo rằng nó không phải là tốt nhất.

Làm xáo trộn:

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

Đọc được:

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

Điều đó khá tốt cho lần thử đầu tiên và dù sao thì độ dài cũng không quá xa so với những người khác. Chắc chắn, thuật toán khá rõ ràng. Lưu ý rằng bạn có thể giảm đáng kể số lượng ký tự bằng cách sử dụng các tên biến có một ký tự!
Noldorin

Cảm ơn. Lỗi cuối cùng của tôi mất một lúc để sửa, nhưng nói chung nó không phải là bất cứ điều gì đáng lo ngại; rất may nó hoạt động đầy đủ.
Robert K

3

Ruby 1.9

(vì regex)

Số ký tự: 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

CHỈNH SỬA: Bao gồm tối ưu hóa của Martin.


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

Thậm chí còn tốt hơn! Tôi đã cố gắng nghĩ ra một cách hay để làm điều đó, và nó đã bỏ qua tâm trí tôi.
Daniel Huckstep

3

SNOBOL4

Số ký tự: 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

Đây là một bán gian lận. Nó sử dụng code()(một biến thể của eval) để tự nén, nhưng không dùng để đánh giá biểu thức đầu vào.

Phiên bản gỡ rối, không có 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

Chiến lược:

  • Đầu tiên, xóa tất cả dấu cách ( spaces)
  • Bất cứ khi nào có thể, hãy xóa dấu ngoặc bao quanh một số ( paren)
  • Nếu không, hãy tìm một biểu thức đơn giản liên quan đến hai số, có tiền tố là '('hoặc ở đầu chuỗi
  • Nếu không có quy tắc nào ở trên áp dụng, biểu thức được đánh giá đầy đủ. Bây giờ nếu đầu vào đã được hình thành tốt, chúng ta nên để lại một số.

Thí dụ:

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

3

C #

Số lượng nhân vật: 355

Tôi đã lấy Câu trả lời của Noldorin và sửa đổi nó, vì vậy hãy cung cấp cho Noldorin 99% tín dụng cho điều này. Tốt nhất tôi có thể làm với thuật toán đang sử dụng là 408 ký tự. Xem Câu trả lời của Noldorin để biết phiên bản mã rõ ràng hơn.

Các thay đổi đã thực hiện:
Thay đổi so sánh ký tự để so sánh với số.
Đã xóa một số khai báo mặc định và kết hợp cùng một loại khai báo.
Làm việc lại một số trạng thái 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;}

Chỉnh sửa: hạ nó xuống một số nữa, từ 361 xuống 355, bằng cách loại bỏ một trong các trạng thái trả lại.


À, tôi không nhận ra là bạn đã đăng nó như một câu trả lời mới. Cảm ơn vì tất cả tín dụng (có lẽ nhiều hơn tôi xứng đáng, vì tôi đã bị mắc kẹt khoảng 390). Tôi sẽ sớm xem xét kỹ hơn các sửa đổi ... điều duy nhất mà tôi cho là thay đổi phép so sánh char để sử dụng số. :)
Noldorin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.