Обчислення рядка простих математичних виразів [закрито]


76

Виклик

Ось виклик (мого власного винаходу, хоча я не здивуюсь, якщо він раніше з’являвся деінде в Інтернеті).

Напишіть функцію, яка приймає один аргумент, який є рядковим поданням простого математичного виразу, і обчислює його як значення з плаваючою точкою. "Простий вираз" може містити будь-що з наведеного: додатні чи від'ємні десяткові числа, + , - , * , / , ( , ) . Вирази використовують (звичайний) інфіксний запис . Оператори повинні оцінюватися в тому порядку, в якому вони з'являються, тобто не так, як у BODMAS , хоча, звичайно , слід правильно дотримуватися дужок. Функція повинна повернути правильний результат для будь-якогоможливий вираз цієї форми. Однак функція не повинна обробляти неправильно сформовані вирази (тобто такі, що мають поганий синтаксис).

Приклади виразів:

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

Правила

Я передбачаю тут якусь форму "обману" / хитрості, тому, будь ласка, дозвольте мені застерегти це! Обманюючи, я маю на увазі використання функції evalабо еквівалентної функції в динамічних мовах, таких як JavaScript або PHP, або рівномірне компіляцію та виконання коду на льоту. (Я думаю, що моя специфікація "немає BODMAS" в значній мірі це гарантувала.) Окрім цього, немає ніяких обмежень. Я передбачаю тут кілька рішень Regex, але було б непогано бачити більше, ніж просто це.

Зараз мене тут в основному цікавить рішення C # /. NET, але будь-яка інша мова теж буде цілком прийнятною (зокрема, F # та Python для функціонального / змішаного підходів). Я ще не вирішив, чи прийму як відповідь найкоротше чи найвигадливіше рішення (принаймні для мови), але я вітаю будь-яку форму рішення будь-якою мовою , крім того, що я щойно заборонив вище !

Моє рішення

Я тепер розмістив мої C # рішення тут (403 символів). Оновлення: Моє нове рішення значно подолало старе на 294 символи за допомогою чудового регулярного виразу! Я підозрював, що деякі з мов, які існують там, з легшим синтаксисом (особливо функціональним / динамічним) можуть бути легко переграні, і це було доведено правильно, але мені було б цікаво, якби хтось міг би перемогти це в C # все-таки.

Оновлення

Я вже бачив дуже хитрі рішення. Дякуємо всім, хто опублікував один. Хоча я ще ні одного з них не тестував, я збираюся довіряти людям і припускати, що вони принаймні працюють з усіма наведеними прикладами.

Тільки для примітки, повторне входження (тобто безпека ниток) не є вимогою для функції, хоча це і бонус.


Формат

Будь ласка, опублікуйте всі відповіді у такому форматі для зручності порівняння:

Мова

Кількість символів: ???

Повністю затуманена функція:

(code here)

Функція очищення / напівзатуманення:

(code here)

Будь-які примітки щодо алгоритму / розумних комбінацій клавіш.



2
Ви, мабуть, мали на увазі, що ваш перший приклад дорівнює .125 (переміщення десяткової коми), а другий - 99 з лівого боку (одна занадто багато дев'яток).
John Y

Так, дякую. Це були досить кричущі помилки.
Noldorin

1
Ви повинні додати приклад, коли відсутність BODMAS є значною, наприклад, "1 + 1 * 3 = 6"
Бен Бланк,

3
Ах, мені було цікаво, коли настане перше голосування для закриття. Зверніть увагу всім виборцям: у StackOverflow вже є безліч відкритих питань щодо гольфу. Здається, вони погоджуються, що вони добре - в основному лише трохи веселощів.
Noldorin

3
Я схильний погодитися, що це нормально, особливо, оскільки "вікі"
Марк Гравелл

Відповіді:


36

Perl (без оцінки)

Кількість символів: 167 106 (див. Нижче версію із 106 символів)

Повністю затуманена функція: (167 символів, якщо ви об’єднаєте ці три рядки в один)

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

Очистити / розмити версію:

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

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

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

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

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

Спочатку я неправильно прочитав правила, тому подав версію з "eval". Ось версія без неї.

Останній біт розуміння, коли я зрозумів , що остання восьмерична цифра в символьних кодах +, -, /і *відрізняється, і що ord(undef)0. Це дозволяє мені створити таблицю відправки в @aвигляді масиву, а просто посилатися на коді в розташування 7 & ord($3).

Існує очевидне місце, щоб поголити ще одного персонажа - перевтілитися q""в ''- але це ускладнило б вирізання і вставку в оболонку.

Ще коротше

Кількість символів: 124 106

Беручи до уваги редагування за ефектом , тепер воно до 124 символів: (об’єднайте два рядки в один)

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

Коротше все-таки

Кількість символів: 110 106

Рубінове рішення знизу штовхає мене далі, хоча я не можу досягти його 104 символів:

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

Мені довелося поступитися і використати ''. Цей рубіновий sendфокус дійсно корисний для вирішення цієї проблеми.

Вичавлюючи воду з каменю

Кількість символів: 106

Невеликий викривлення, щоб уникнути перевірки ділення на нуль.

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

Ось тестовий джгут для цієї функції:

perl -le 'sub e{($_)=@_;$n='\''( *-?[.\d]++ *)'\'';s:\($n\)|$n(.)$n:($1,0,$2*$4,$2+$4,0,$2-$4)[7&ord$3]//$2/$4:e?e($_):$_}' -e 'print e($_) for @ARGV' '1 + 3' '1 + ((123 * 3 - 69) / 100)' '4 * (9 - 4) / (2 * 6 - 2) + 8' '2*3*4*5+99' '2.45/8.5*9.27+(5*0.0023) ' '1 + 3 / -8'

3
Дуже страшно, наскільки маленький Perl може піти, я відредагував свою відповідь, щоб вона була найменшою реалізацією Ruby, і не вистачило місця на 170 символів. Але 124? Хороша підлива!
Роберт К.

1
Я не помітив, що про це ще ніхто не згадував, але для цього рішення потрібен Perl 5.10. Для сумісності з 5.8 використовуйте (-? (?> \ D + (\. \ D +)?)), Яке довше на два символи.
ефемієнт

7
@Epaga, не хвилюйся, я отримав вашу помилку: perl. є. дивовижний.
Денні 02

1
Скоротіть його на 1 символ - змініть "$ _ = $ _ [0]" на "($ _) = @ _".
Кріс Лутц,

1
Оскільки він безумовно виконує арифметику незалежно від оператора (вибір правильного результату пізніше), йому потрібно уникати ділення на нуль.
ефемієнт

44

Монтажник

427 байт

Заплутаний, зібраний із чудовим A86 у виконуваний файл .com:

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

РЕДАКТУВАТИ : Пряме джерело:

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

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

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



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

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

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

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

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

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

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

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


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


    d1: db '0123456789.-+/*()', 32, 9
    d3: dw 10
    op: db 0e9h, 0c1h, 0f9h, 0c9h
    cw: dw 0f7fh
    zero: dw 0
    res:db 'Error$'
    s:  dw (offset s)+2

1
Асамблея - це справжнє програмування!
Андреас Рейбранд,

Одного разу я бачив повну гру в тетріс на 64 байти
BlueRaja - Danny Pflughoeft

29

Рубін

Кількість символів: 103

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

Це нерекурсивна версія рішення “Злої блохи”. Підвираз у дужках оцінюється знизу вгору, а не зверху вниз.

Редагувати : перетворення "while" на умовну + хвостову рекурсію зберегло кілька символів, тому воно більше не є рекурсивним (хоча рекурсія не є семантично необхідною).

Редагувати : Позичення ідеї Даніеля Мартіна об’єднати регулярні вирази економить ще 11 символів!

Редагувати : Ця рекурсія навіть корисніша, ніж я спочатку думав! x.to_fможе бути переписаний як e(x), якщо xвипадково містить одне число.

Редагувати : Використання ' or' замість ' ||' дозволяє скинути пару дужок.

Довга версія:

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

# The evaluation function
def e(x)
  matched = x.sub!(/\(#{N}\)|#{N}([^\d.])#{N}/) do
    # Group 1 is a numeric literal in parentheses.  If this is present then
    # just return it.
    if $1
      $1
    # Otherwise, $3 is an operator symbol and $2 and $4 are the operands
    else
      # Recursively call e to parse the operands (we already know from the
      # regexp that they are numeric literals, and this is slightly shorter
      # than using :to_f)
      e($2).send($3, e($4))
      # We could have converted $3 to a symbol ($3.to_s) or converted the
      # result back to string form, but both are done automatically anyway
    end
  end
  if matched then
    # We did one reduction. Now recurse back and look for more.
    e(x)
  else
    # If the string doesn't look like a non-trivial expression, assume it is a
    # string representation of a real number and attempt to parse it
    x.to_f
  end
end

Я майже думав, що це новий лідер, поки не побачив, що редакція Perl стала ще коротшою! Хороша робота, як би там не було.
Noldorin

1
Позбавлення від 'e = readline.chomp; ...; p e.to_f' та використання 'def q (e); ...; e.to_f; end', як інші рішення, дозволить зберегти 10 символів. Однак він не може отримати значення q ("1 + 3 / -8") == - 0,5, як у питанні.
ефемієнт

@ephemient - це помилка, яку ви знайшли - вона не могла обробляти від’ємні числа.
finnw

1
Gsub! ('-', '') у моєму коді стосується того, як працює дужка, якщо її заперечити. Якщо результат інтер’єру запереченої дужки від’ємний, мінус поза твердженням залишається: --7,0, наприклад. Однак підтримка, яка коштує мені 24 символів, все ще на 19 вище вас. Я не знаю, що я можу зменшити його не більше, ніж трюки, які я навчився від вас. (Але я
Роберт К

1
Використання "відправити" насправді наближається до порушення правила "не оцінювати". Але приємний трюк, включаючи пробіли у ваше регулярне вираження чисел. Використовуючи цей фокус та ще один, моє рішення perl зменшилось до 119 символів.
Даніель Мартін,

29

C (VS2005)

Кількість символів: 1360

Зловживання препроцесором та попередження щодо розкладки коду для забави (прокрутіть униз, щоб побачити):

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

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

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

25

Visual Basic.NET

Кількість символів: 9759

Я сам більше боулер.

ПРИМІТКА: не враховує вкладені дужки. Також не перевірено, але я впевнений, що це працює.

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

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

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

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

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

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

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

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

    Return result

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

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

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

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

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

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

        Dim numberToReturn As New Number
        numberToReturn = Number.ConvertDoubleToNumber(result)
        Return numberToReturn
    End Function
End Class
End Class

2
Я також, напевно, міг би вдарити 10 тисяч символів, якби не так пізно ввечері :)
Джейсон

Чи знаєте ви, що менше символів - це краще? Таким чином вони ніколи не вважають, що vb.net нічого доброго.
Ікке,

11
@ikke - це повинно було бути якомога менше символів? о боже ... хтось, здається, пропустив суть
Джейсон

3
ZERO_STRING_REPRESENTATION виглядає як щось, що належить thedailywtf
erikkallen,

1
+1 це змусило мене сміятися більше, ніж будь-яка інша відповідь на SO. ніколи .
R .. GitHub СТОП ДОПОМОГАЙ ЛЕД

25

Хаскелл

Кількість символів: 182

Жодної спроби розумності, лише деяке стиснення: 4 рядки, 312 байт.

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

А тепер, справді потрапляючи в дух гольфу, 3 рядки та 182 байти:

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

Вибухнув:

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

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

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

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

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

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

-- If there’s no operation parsable, just return.
oper str num = (str, num)

Я підозрюю, що, можливо, все ще можна опуститися нижче 225, але це наскільки я можу дістатись о 3 ранку.
ефемієнт

Це видається цілком елегантним функціональним рішенням. (Той, котрий я, звичайно, не зрозумів би без коментарів, тож дякую за це.) Крім того, на даний момент ви лише незначно випереджаєте рішення Дейва Python, тому ви, здається, лідируєте! Мені було б цікаво, чи може рішення F # збігатися чи навіть перевершувати це.
Noldorin

Мені цікаво, що рішення Парсека (комбінатори синтаксичного аналізу = узагальнений регулярний вираз + більше), навіть якщо була зроблена спроба мінімізації, не наближається до ручного розбору. Я не думаю, що синтаксис F # може бути таким стислим, як Хаскелл, але я також вітаю деякі змагання :)
ефемієнт

@ephemient: Я спробував швидко використовувати як регулярний вираз, так і модуль синтаксичного аналізатора в Python. Був майже відразу нижче 300 символів, але не бачив шансів отримати конкуренцію. Проблема полягає в імпорті, і виклики функції з’їдають занадто багато. Це справедливо для більшості мов (виняток Perl). До речі, вам не потрібно розбирати себе, щоб отримати значно менше 300 символів, як показує моє рішення.
Стефан

2
Я думаю, ви перестали карати за кількість персонажів. Проблема вимагала функції String-> Double, тому вам слід підрахувати символи, замінивши "main = interact $ show". з "q =", для ще 17 символів, підраховуючи ваш рахунок до 209.
Даніель Мартін

23

Python

Кількість символів: 237

Повністю затуманена функція:

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

Функція очищення / напівзатуманення:

import operator

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

    answer = 0

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

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

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

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

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

    return answer

Примітка: поточна версія не обробляє введення типу - (1.0), хоча вона правильно обробляє негативні літерали. З специфікації не було зрозуміло, чи потрібно це робити.
Дейв

Можна зробити l неглобальним безкоштовно, включивши його до списку параметрів e. Проте все одно не буде безпечним для потоків.
Дейв

Дуже хитра. Це було варте зусиль перекладу. :)
Нік Джонсон,

@Dave: Моє -(1.0)теж не працює, тому не хвилюйтесь! Я уточню питання. У будь-якому випадку, здається дуже розумним рішенням - я все ще намагаюся зрозуміти, як це працює (хоча я не зовсім знаю Python). Якби ви могли додати коротке пояснення, це було б дуже вдячне.
Noldorin

20

Fortran 77 (діалект gfortran, тепер з підтримкою g77)

Кількість символів: 2059

Заплутана версія:

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

Очищена версія: (3340 символів з помостом)

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

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

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

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

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

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

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

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

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

Примітки Ця відредагована версія скоріше зліша, ніж моя перша спроба. Той самий алгоритм, але тепер вбудований у жахливий клубок gotos. Я відмовився від спільних процедур, але зараз використовую кілька варіантів обчислюваних гілок. Усі перевірки помилок та звітування були вилучені, але ця версія мовчки оговтається від деяких класів несподіваних символів у введенні. Ця версія також компілюється з g77.

Основними обмеженнями залишаються жорстке форматування fortran, довгі та всюдисущі ключові слова та прості примітиви.


14
Боже добрий, людино! Напевно, вам сьогодні було нудно. ;)
gnovice

2
Хе-хе, я не думаю, що я ніколи очікував рішення Fortran! Я думаю, ми можемо зробити висновок, що мова не особливо підходить для кодування гольфу? У будь-якому випадку голосували за чисті зусилля та за використання застарілої мови. :)
Noldorin

Я вважаю, що таке непомітне байд-дилювання є багатослівним і незграбним у fortran, але насправді не важким. Написання неструктурованого коду та використання цих обчислених гілок, навпаки, здається дивним.
dmckee --- екс-модератор кошеня

Чудово зроблено, але як версія fortran із 2000+ символів набирає більше голосів, ніж моя коротка маленька версія ruby1.9? лол
Даніель Хакстеп

@darkhelmet: Я поняття не маю. Я зробив це на жайворонка і очікував одного-двох голосів за зусилля та збоченість. Я непристойно пишаюся цією гидотою, але це смішно ...
dmckee --- екс-модератор кошеня

17

C99

Кількість символів: 239 (Але див. Нижче 209 )

стиснута функція:

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

декомпресована функція:

float strtof();

char* e;
float v();

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

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

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

Функція не є повторною.

EDIT від Кріса Луца : Я ненавиджу топтати код іншої людини, але ось версія з 209 символів:

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

Читається (ну, насправді не дуже читається, але розпаковано):

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

Так, f()це макрос, а не функція, але він працює. У читаній версії частина логіки переписана, але не перевпорядкована (наприклад, o != '+'замість o - '+'), але в іншому випадку це просто відступ (і попередньо оброблена) версія іншої. Я постійно намагаюся спростити if(!o|o==41)return a;деталь у for()циклі, але це ніколи не робить її коротшою. Я все ще вірю, що це можна зробити, але я закінчив гольф. Якщо я більше працюватиму над цим питанням, воно буде на тій мові, яку не можна називати .


Приємне рішення та бонусні бали за використання "чистого" C. Також б'є моє на 3 символи! Повторне входження в правила не було, тож це нормально. (Однак це плюс.)
Noldorin

Приємно! Ви можете поголити ще кілька символів, використовуючи коди ASCII, наприклад, замініть "0" на 48 і т. Д. І звичайно, ви можете заощадити купу, використовуючи atof () замість доморощеного флоат-синтаксичного аналізатора, але ви навмисне не використовувати бібліотечні функції, що не є суворою вимогою проблеми.
Адам Розенфілд,

Я думав використовувати atof (), але він не говорить вам, де закінчується рядок з плаваючою ланкою, тому вам все одно потрібно буде проаналізувати його.
Ферруччо

Дякую за підказку, Адаме. Користуючись цим та кількома іншими (потворними) трюками, я трохи зменшив його.
Ферруччо

Ой, я не розраховував на негативні числа. Код завищений до 400 символів.
Ферруччо

13

Загальний Lisp

(SBCL)
Кількість символів: 251

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

Правильна версія (387 символів):

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

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

Введення - це форма w(), яка приймає один аргумент рядка. Він використовує фокус, що число / операнди та оператори мають шаблон NONON ... і рекурсивно обчислює всі операнди, а отже, вкладання дуже дешеве. ;)


Розумне рішення. Тим не менше, я не зовсім впевнений, що він повністю дійсний, враховуючи, що специфікація полягала в тому, щоб функція приймала об'єкт рядка.
Нолдорін

Вибач за це. Виправлено!

Нема проблем. Не розумів, що перетворення було настільки легким. Хороше рішення, все-таки!
Noldorin

Ого. Це гарно. :)
Emil H

11

JavaScript (не сумісний з IE)

Кількість символів: 268/260

Повністю затуманена функція:

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

або, в JavaScript 1.8 (Firefox 3+), ви можете зберегти кілька символів, використовуючи закриття виразів:

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

Функція очищення / напівзатуманення:

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

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

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

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

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

    return expression();
}

Жодна з версій не буде працювати в IE, оскільки вони використовують підписку у вигляді масиву на рядок. Якщо замінити обидва входження x[0]з x.charAt(0), першим має працювати всюди.

Я вирізав ще кілька символів з першої версії, перетворивши змінні у параметри функції та замінивши інший оператор if умовним оператором.


Це досить добре. Я чекав, коли хтось використає тут регулярний вираз. :) Здається, динамічні мови безумовно мають перевагу в цій проблемі.
Noldorin

8

C # з любов'ю Regex

Кількість символів: 384

Повністю затуманений:

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

Не затуманено:

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

    float result = 0;

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

    return result;
}

Скористається перевагами функції балансування групи Regex .NET .


Дякую за це рішення. :) Я не був впевнений, чи побачу я рішення C # з регулярним виразом, але ось у нас воно є. Тепер можна сперечатися, чи слід вам включати "using System.Text.RegularExpressions;" у вашому рахунку символів, але це все ж хороше рішення.
Noldorin

Це не було частиною правил :). Якщо ви додасте "з використанням R = System.Text.RegularExpressions.Regex;" і замінити мій "Regex" на R, він переходить на 417.
Джефф Мозер,

@Jeff: Ну, технічно він не компілюється без оператора using, тому за замовчуванням він повинен бути включений. Однак, зважаючи на те, що всі наші рішення C # значно відстають від лідера.
Noldorin

8

PHP

Кількість символів: 284

затуманений:

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

читабельно:

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


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

Має працювати з будь-яким дійсним введенням (включаючи від’ємні числа та довільний пробіл)


preg_replace()з eмодифікатором заощадить вам ще кілька байтів.
Алікс Аксель

8

SQL (SQL Server 2008)

Кількість символів: 4202

Повністю затуманена функція:

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

Функція очищення / напівзатуманення:

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

Це не найкоротше. Але я думаю, що це дуже гнучко для SQL. Додавати нових операторів легко. Змінити пріоритет операторів легко.


1
Боже, я не думаю, що я ніколи очікував рішення SQL! Це не повністю в дусі кодового гольфу, але в будь-якому випадку голосовано за зухвалість (і навіть не за допомогою мови програмування). :)
Нолдорін

2
@Noldorin, чому це не в дусі кодового гольфу?
tuinstoel

6

F #

Кількість символів: 327

OP шукав версію F #, ось вона. Це можна зробити набагато приємніше, оскільки я тут зловживаю посиланням, щоб зберегти символи. Він обробляє більшість речей, таких як - (1.0) , 3 - -3 і навіть 0 - .5 тощо.

let g s=
 let c=ref[for x in System.Text.RegularExpressions.Regex.Matches(s,"[0-9.]+|[^\s]")->x.Value]
 let rec e v=if (!c).IsEmpty then v else 
  let h=(!c).Head
  c:=(!c).Tail
  match h with|"("->e(e 0.0)|")"->v|"+"->e(v+(e 0.0))|"-"->e(v-(e 0.0))|"/"->e(v/(e 0.0))|"*"->e(v*(e 0.0))|x->float x
 e(e 0.0)

Справді, я сподівався на рішення F #. Дякую за це. Кількість символів теж досить пристойна, особливо враховуючи, що "System.Text.RegularExpressions.Regex.Matches" займає абсурдну кількість символів.
Noldorin

так, те саме з викликами .Value.IsEmpty / Tail / Head - у мене є нова версія в роботі; p сподіваючись на символи sub250.
Чет

Я насправді не впевнений, чи дозволено вам у деяких змаганнях з гольфу на коді імпортувати / використовувати заяви за межами підрахунку символів. Це точно допомогло б, якщо так. :) Будемо раді побачити нову версію.
Noldorin

@Noldorin: Ні, вибачте, я не можу зрозуміти це під 327 символів цього (трохи вдосконаленого з минулого) коду. Вигода від того, що все ідеально розібрано з регулярним виразом, перевищує шалено довгу назву "System.Text.RegularExpressions.Regex.Matches" Якби F # мав коротке (псевдонімне) ім'я функції "Збіги", я б мав 288 символів, але це не = /.
Чет

@fredrikholmstrom: Не хвилюйтеся - тим не менше, хороше рішення. Крім того, я не зовсім впевнений, але я б сказав, що ви повинні мати можливість перемістити "System.Text.RegularExpressions" у "відкритий" вираз і виключити кількість символів для цього принаймні.
Нолдорін

6

J

Кількість символів: 208

Після коментаря Джеффа Мозера я зрозумів, що зовсім забув про цю мову ... Я не фахівець, але моя перша спроба пройшла досить добре.

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

Це трохи дратує, маючи карту x/yі -zв J x%yі _z. Без цього, можливо, 50% цього коду може зникнути.


Так, це досить приємно. А як щодо рішення в K? : P Я підозрюю, що, можливо, навіть вдасться перемогти Perl.
Noldorin

Woohoo, мені вдалося отримати своє рішення Haskell під моє рішення J! Хоча якби хтось тут був майстром J, K або APL, він, мабуть, знищив би 200-символьний бар'єр ...
ефемієнт

6

Python (не імпортуючи нічого)

Кількість символів: 222

Я вкрав багато хитрощів з відповіді Дейва, але мені вдалося поголити ще деяких персонажів.

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

Коментована версія:

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

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

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

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

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

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

    return n

+1 Приємна ідея дикту. Однак поточна версія не працює на e ('1 + 0'). Замість цього використовуйте {'+': n + m, '-': nm, ' ': n m, '/': n / m, якщо m ще 1}. Я запозичив вашу ідею (з цією поправкою). Дякую
Стефан

Дякую. Я не думав про проблему DivZero; 7-значне виправлення - n / (m або 1).
krubo

Зроби це і для моєї програми ;-)
Стефан

хе-хе, зараз нічого не міняйте, кількість символів прекрасна :)
Тета

5

C #

Кількість символів: 403

Отже, ось моє рішення ... Я все ще чекаю, поки хтось опублікує таке в C #, хто зможе його обіграти. (Марк Гравелл був поруч і, можливо, все-таки зробиться краще за мене після ще кількох роздумів.)

Повністю затуманена функція:

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

Напівзатуманена функція:

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

Здавалося б, нічого надто розумного тут не відбувається. Однак перевага функції полягає в тому, що вона повторно входить (тобто безпечна для потоків).

Я також достатньо задоволений кількістю символів, враховуючи, що це написано на C # (я вважаю дійсним 1.0, 2.0 та 3.0).


Будуть вітатися будь-які поради щодо того, як я можу ще зменшити кількість знаків. (Це моя перша реальна спроба
кодувати

Я отримав <400, але це не відповідає відредагованому тесту, який ви додали
;-p

Пропозиції: "var" для float, char - голить лише кілька і втрачає сумісність C # 1.2 / 2.0.
Марк Гравелл

@Marc: Так, це приблизно стільки, скільки я теж отримав. З кількома іншими незначними змінами, я міг би дійти до 390, але не менше.
Noldorin

Хороший розчин Нолорин. Мені вдалося довести ваше рішення до 361
Кріс Персічетті,

5

Ось ще один:

Сценарій оболонки (за допомогою sed + awk)

Кількість символів: 295

затуманений:

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

читабельний

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

Тест:

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

Результат:

2.68344117647058823526
2.68344

Я (майже) не уявляю, як це працює, але я вражений тим, наскільки добре це виконує сценарій оболонки! Молодці справді.
Noldorin

Ну, просто пам’ятайте, що багато багатьох операційних систем використовують цю суміш мови / інструментів для багатьох різноманітних завдань :)
soulmerge

5

MATLAB (v7.8.0)

Кількість символів: 239

Заплутана функція:

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

Функція очищення (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

Тест:

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

ans =

   -0.5000
  219.0000
   10.0000
    4.0000
    2.6834

Зміст: суміш регулярних виразів та рекурсії. Практично найкраще, що я міг зробити на сьогоднішній день, без обману та використання EVAL.


5

Рубін

Кількість символів: 170

Смутний:

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

Читається:

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

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

Немає справжнього заплутування цього, який я вирішив опублікувати свіжим, оскільки він дико відрізняється від мого першого. Я мав це побачити з самого початку. Процес - це дуже простий процес усунення: знайдіть і вирішіть найвищу пару дужок (найбільш вкладену) в число, поки більше не знайдете, а потім вирішіть усі наявні числа та операції в результаті. І, вирішуючи дужки, я видаляю всі подвійні тире (Float.to_f не знає, що з ними робити).

Отже, він підтримує додатні та від’ємні числа (+3, 3, & -3) і навіть заперечені підвирази в дужках лише за порядком обробки. Єдиною коротшою реалізацією є Perl (без оцінки).

Редагувати: Я все ще переслідую Perl, але зараз це друга найменша відповідь. Я зменшив його, змінивши другий регулярний вираз і змінивши обробку рядка на руйнівний (замінює старий рядок). Це позбавило потреби дублювати рядок, який, як я виявив, був просто новим вказівником на рядок. А перейменувавши функцію на s з рішення, зберегло кілька символів.


Приємна робота, здивований, що я сам не пробував такий підхід, оскільки використовував щось дуже подібне для вирішення іншого питання синтаксичного аналізу.
Mike Tunnicliffe 02

Перегляньте моє рішення щодо способу стиснення цього регулярного виразу. Вам також не знадобиться остання "смужка". І це не схоже на те, що ви повністю реалізуєте одинарний мінус, тому ви отримуєте невелику користь від gsub ('-', '').
finnw

Насправді я не можу скоротити свій конкретний алгоритм або не проходжу 3-4 тести, я не впевнений, чому. Але я міг би зменшити його приблизно на 20 символів.
Роберт К

4

Python з регулярними виразами

Кількість символів: 283

Повністю затуманена функція:

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

Не затуманено:

import re
from operator import *

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

Я хотів побачити, чи не обіграв я інші рішення Python, використовуючи регулярні вирази.

Не вдалося.

Регулярний вираз, який я використовую, створює список пар (val, op), де дійсний лише один елемент у кожній парі. Решта коду - це досить стандартний синтаксичний аналізатор на основі стека з акуратним трюком заміни 3-х верхніх комірок у стеці результатом обчислення за допомогою синтаксису призначення списку Python. Для створення цієї роботи з від’ємними числами потрібно було лише два додаткові символи (-? У регулярному виразі).


Ви можете зберегти пару байт, видаливши "()" з рядка вашого оператора; zipзупиняється в кінці коротшого списку.
Ben Blank

@gooli: Ви використовуєте Windows? За моїм підрахунком, розміщене рішення - лише 273. Одним із пояснень цього може бути те, що ви зарахували нові рядки як два символи в кожному. (Python байдуже, чи є у вас нові символи нового рядка, навіть у Windows.) Іншим поясненням є те, що вдарили 8, коли ви мали на увазі 7.;)
John Y

4

Python

Кількість символів: 382

Ще одне рішення Python, що активно використовує заміну регулярних виразів. При кожному прогоні циклу обчислюються найпростіші вирази, а результати повертаються в рядок.

Це незамінений код, якщо ви не вважаєте регулярні вирази заплутаними.

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

Була ця ідея саме тоді, коли я звертався, і не міг її відпустити, поки я не записав її і не змусив її спрацювати.


1
Гарне рішення ... Мені теж це виглядає дуже зрозуміло. Здавалося б, використання dict / zip для зберігання операторів - це, безумовно, дуже ефективний підхід у Python.
Noldorin

4

C #

Кількість символів: 396 (оновлено)

(але не проходить тест, який ви додали за допомогою "/ -8", і я не схильний його виправляти ...

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

Від:

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

Ах чудово, рішення на C #. Ваше використання зокрема типів, що дозволяють обнулювати, є досить цікавим. 484 здається досить непоганим, враховуючи, що у вас не було часу навести порядок. (Одним із покращень було б перетворення оператора switch у ряд ifs, я вважаю.) Я опублікував своє власне рішення C #, якщо ви хочете порівняти. :)
Нолдорін

4

Python

Кількість символів: 235

Повністю затуманена функція:

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

Напівзатуманене:

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

FWIW, n + 1-е рішення Python. У відвертому зловживанні спробою, крім того, я використовую підхід методом спроб і помилок. Він повинен обробляти всі випадки правильно , включаючи такі речі , як -(8), --8і g('-(1 - 3)'). Це повторний вхід. Без підтримки --справи, яку багато реалізацій не підтримують, вона становить 217 символів (див. Попередню редакцію).

Дякую за цікаву годину в неділю та ще 30 хвилин у понеділок. Дякую Крубо за його приємний дикт .


Ще один цікавий підхід ... Ідентичний за довжиною одному з інших рішень Python теж. Це підтверджує мою думку, що використання словника операторів - це шлях, де це можливо. Я хотів зробити щось подібне в C #, але синтаксис просто займає занадто багато символів.
Нолдорін

4

Рубін

Кількість символів: 217 179

Це найкоротше рубінове рішення на сьогоднішній день (одне, що базується на RegExp, дає неправильні відповіді, коли рядок містить кілька груп дужок)- вже неправда. Рішення на основі регулярного виразу та заміни коротші. Цей заснований на стосі акумуляторів і аналізує весь вираз зліва направо. Він повторно входить і не змінює вхідний рядок. Його можна звинуватити в порушенні правил невикористання eval, оскільки він називає Floatметоди з однаковими іменами як їх математичні мнемоніки (+, -, /, *).

Заплутаний код (стара версія, налаштована нижче) :

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

Більш заплутаний код:

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

Чистий код:

def f(p)
  accumulators, operands = [0], ['+']
  p.gsub(/-/,'+-').scan(/(?:(-?\d+(?:\.\d+)?)|(.))\s*/).each do |n|
    number, operand = n
    case operand
      when '('
        accumulators << 0
        operands << '+'
      when ')'
        number = accumulators.pop
        operands.pop
      else 
        operands[-1] = operand
    end if number.nil?
    accumulators[-1] = accumulators.last.method(operands[-1]).call(number.to_f) unless number.nil?
  end
  accumulators.first
end

Насправді, мій коротший (198) і використовує регулярний вираз для розв’язування дужок зверху вниз до остаточного результату математики. Отже, "3 + (3 * (3 + 9))" йде: "3 + (3 * 12)", "3 + 36", 39. Він йде зверху вниз, зліва направо. Він вирішує всі тести, за винятком одного незначного контролю, який вимагає пробілів між маркерами. Див .: stackoverflow.com/questions/928563/…
Роберт К

Не те, щоб твій не був розумним, він дуже є.
Роберт К.

(3 + 7) - (5 + 2) - це те, що я мав на увазі під кількома групами дужок. Ваше рішення має проблему з невкладеними дужками через жадібність регулярного виразу.
samuil

Це може бути, але я вчора ввечері возився з парсером і вдосконалив його в своїй системі (за допомогою математичних функцій та змінних з однієї літери). Тож я витягнув з нього свій кращий регулярний вираз, і він працює чудово, пост також оновлюється новим підрахунком символів. ;-) Миттєво підкажу ваше рівняння до тестів у відповіді.
Роберт К.

Я не думаю, що використання 'method' або 'send' є обманом - це просто пошук таблиці - ви не використовуєте вбудований парсер.
finnw

3

Рубін 1.8.7

Кількість символів: 620

Спробуйте полегшити мою реалізацію, це перший раз, коли я написав парсер виразів у своєму житті! Я гарантую, що це не найкраще.

Смутний:

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

Читається:

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

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

  # now for some solutions!
  i = 1
  a = parts[0].to_f # left-most value is will become the result
  while i < parts.count
    b,c = parts[i..i+1]
    c = c.to_f
    case b
      when '+': a = a + c
      when '-': a = a - c
      when '*': a = a * c
      when '/': a = a / c
    end
    i += 2
  end
  a
end

Це цілком непогано для першої спроби, і довжина в будь-якому випадку не сильно відрізняється від інших. Звичайно, алгоритм досить чіткий. Зверніть увагу, що ви можете значно зменшити кількість символів, просто використовуючи однобуквенні імена змінних!
Noldorin

Дякую. Моя остання помилка зайняла деякий час, щоб виправити, але загалом це не було нічим страшним; на щастя, він функціонує повноцінно.
Роберт К.

3

Рубін 1.9

(через регулярний вираз)

Кількість символів: 296

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

EDIT: Включає оптимізацію Мартіна.


r = m [1] .to_f.send (m [3], m [4] .to_f), якщо% w {+ - * /}.include?m[3]
Мартін Карпентер

Навіть краще! Я намагався придумати хороший спосіб зробити це, і це не сприймало мене.
Даніель Хакстеп,

3

SNOBOL4

Кількість символів: 232

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

Це напівкруха. Він використовує code()(варіант eval), щоб декомпресувати себе, але не для оцінки вхідного виразу.

Безмовна версія, без code:

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

Стратегія:

  • Спочатку видаліть усі пробіли ( spaces)
  • По можливості видаляйте дужки, що оточують число ( paren)
  • В іншому випадку знайдіть простий вираз, що включає два числа, з префіксом '('або на початку рядка
  • Якщо жодне з наведених правил не застосовується, вираз повністю обчислюється. Тепер, якщо вхідні дані були добре сформовані, нам слід залишити число.

Приклад:

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

3

C #

Кількість символів: 355

Я взяв відповідь Нолдоріна і змінив його, тому дайте Нолдоріну 99% заслуги за це. Найкраще, що я міг зробити з використанням алгоритму, було 408 символів. Дивіться Відповідь Нолдоріна для більш чіткої версії коду.

Внесені зміни:
Змініть порівняння символів для порівняння з числами.
Видалено деякі декларації за замовчуванням та об’єднано однотипні декларації.
Повторно опрацювали деякі з атрибутів if.

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

Редагувати: збив його ще з 361 до 355, вилучивши один із повернених статусів.


Ах, я не здогадувався, що ви вже розмістили це як нову відповідь. Дякую за всі заслуги (що, мабуть, більше, ніж я заслуговую, оскільки я застряг близько 390). Незабаром я детальніше подивлюсь на модифікації ... єдиною, яку я вважав, було зміна порівняння символів на використання чисел. :)
Noldorin
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.