Оценка строки простых математических выражений

Вызов

Вот проблема (мое собственное изобретение, хотя я не удивлюсь, если раньше оно появилось где-нибудь в Интернете).

Напишите функцию, которая принимает единственный аргумент, представляющий собой строковое представление простого математического выражения, и оценивает его как значение с плавающей запятой. "Простое выражение" может включать любое из следующего: положительные или отрицательные десятичные числа, +, -, *, / < / strong>, (, ). Выражения используют (обычно) инфиксную нотацию. Операторы следует оценивать в том порядке, в котором они появляются, т. Е. не, как в 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)

Любые заметки по алгоритму / умным ярлыкам, которые он использует.



person Community    schedule 29.05.2009    source источник
comment
Вероятно, вы имели в виду, что ваш первый пример равняется 0,125 (переместите десятичный разряд), а во втором - 99 слева (слишком много девяток).   -  person John Y    schedule 30.05.2009
comment
Да спасибо. Это были довольно вопиющие опечатки.   -  person Noldorin    schedule 30.05.2009
comment
Я также сделал эту вики сообщества (изначально забыл сделать это), так как это кажется наиболее подходящим для этого стиля вопросов.   -  person Noldorin    schedule 30.05.2009
comment
Самое забавное здесь то, что, не указав BODMAS, вы исключили самые простые решения. В PHP: функция calc ($ string) {return eval ($ string); }   -  person jmucchiello    schedule 30.05.2009
comment
@jmucchiello: Да, я действительно устранил самые простые (читерские?) решения! Я упоминаю в вопросе, что мне не очень интересно видеть решения, использующие функции динамической оценки (например, eval), так что все в порядке. :)   -  person Noldorin    schedule 30.05.2009
comment
Вы должны добавить пример, в котором отсутствие BODMAS является значительным, например 1 + 1 * 3 = 6   -  person Ben Blank    schedule 30.05.2009
comment
@Ben: Хороший момент. Я изменю одну из них как таковую.   -  person Noldorin    schedule 30.05.2009
comment
Ааа, мне было интересно, когда состоится первое голосование по закрытию. На заметку всем избирателям: в StackOverflow уже есть много вопросов о гольфах с открытым кодом. Консенсус кажется, что они в порядке - в основном просто немного повеселиться.   -  person Noldorin    schedule 30.05.2009
comment
Я склонен согласиться с тем, что это нормально, тем более что вики   -  person Marc Gravell    schedule 30.05.2009
comment
Спасибо за одобрение модератора, Марк. :)   -  person Noldorin    schedule 30.05.2009
comment
Я предполагаю, что кто-то сделает это на J, состоящем из 25 символов.   -  person Jeff Moser    schedule 31.05.2009
comment
Я играл с парой решений MATLAB, которые могут приближаться к 200 символам, но я не могу протестировать их до понедельника. знак равно   -  person gnovice    schedule 31.05.2009
comment
@gnovice: Ах, я ждал решения MATLAB. Было бы хорошо увидеть один - просто опубликуйте его, когда сможете.   -  person Noldorin    schedule 31.05.2009
comment
Странный вопрос в том смысле, что вы ищете алгоритмы, которые дают неверные / неожиданные результаты. Если вы не подчиняетесь BODMAS (я выучил BEDMAS (E = экспоненты), то это бесполезно для людей, которые хотят запрограммировать правильный алгоритм.   -  person Kibbee    schedule 31.05.2009
comment
@Kibbee: Не совсем странный вопрос, если вы правильно его понимаете. Я так понимаю, вы не знакомы с кодом для гольфа? Цель состоит в том, чтобы немного повеселиться, пытаясь написать кратчайшее решение! Использование в реальном мире значения не имеет. Я ввел требование отсутствия BODMAS по двум причинам: а) чтобы упростить алгоритмы (в общем случае), б) чтобы попытаться остановить людей, просто использующих eval. Кроме того, нет ничего неправильного в том, чтобы не следовать BODMAS - это чисто соглашение. (БОДМЫ и СПАЛЬНИ, кстати, приемлемы.)   -  person Noldorin    schedule 31.05.2009
comment
Кто-то длиннее меня в зубе должен сделать 6-символьный раствор APL.   -  person Chris Lutz    schedule 02.06.2009
comment
Думаю, сейчас полезно добавить тег perl ...   -  person ilya n.    schedule 08.06.2009


Ответы (43)


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

С учетом правок, внесенных ephemient, теперь он сократился до 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'
person Community    schedule 31.05.2009
comment
Это безумие. Я полагаю, что исходный код языка гольфа должен был победить их всех, и это действительно так. Не могу сказать, что мне очень нравится обилие регулярных выражений, но хорошая работа! Возможно, мне придется принять это как ответ ... - person Noldorin; 31.05.2009
comment
это хитро, используя ord. Мне нравится, как вы использовали $ n как способ дублировать часть регулярного выражения. - person Ape-inago; 31.05.2009
comment
Есть ли причина, по которой вы используете local, когда мой должен работать так же хорошо? - person Chris Lutz; 01.06.2009
comment
@Chris Lutz: Нет, поэтому я изменил его и потерял еще трех персонажей. Просто сила сильной привычки подсказывала мне использовать локальные с глобальными переменными. - person Daniel Martin; 01.06.2009
comment
Ух ... в моей редакции qw // должно было быть qr //. Поскольку это (случайно) работает, вы можете убрать еще 2 символа, заменив его на ''. - person ephemient; 01.06.2009
comment
Фактически, эфементно, я уже делал это (используя q). Но хороший трюк, позволяющий избежать лишних шумов. - person Daniel Martin; 01.06.2009
comment
Это впечатляет - вы сбрили его и добавили защиту от деления на ноль. Я собирался предложить использовать 1 while вместо while () {}, но я не знал, что 1 while (без пробела) будет правильно разбираться, поэтому он не экономил место. Рад, что это работает. - person Chris Lutz; 02.06.2009
comment
Довольно страшно, насколько маленьким может быть Perl, я отредактировал свой ответ, чтобы он оставался самой маленькой реализацией Ruby, и у меня закончилось место на 170 символов. Но 124? Хорошая подливка! - person Robert K; 02.06.2009
comment
Я не заметил, что об этом еще никто не упомянул, но для этого решения требуется Perl 5.10. Для совместимости с 5.8 используйте (-? (? ›\ D + (\. \ D +)?)), Который на два символа длиннее. - person ephemient; 02.06.2009
comment
@Epaga, не волнуйся, я понял твою опечатку: perl. является. классно. - person Danny; 02.06.2009
comment
Сократите его на 1 символ - замените $ _ = $ _ [0] на ($ _) = @ _. - person Chris Lutz; 05.06.2009
comment
Неужели нужно проверять деление на ноль? У большинства решений (в том числе и у меня) нет. - person finnw; 05.06.2009
comment
Поскольку он безоговорочно выполняет арифметические действия независимо от оператора (правильный результат выбирается позже), ему действительно нужно избегать деления на ноль. - person ephemient; 05.06.2009
comment
Использовал еще один прием Perl 5.10, чтобы немного сократить количество персонажей, но, черт возьми, это становится действительно сложно. Добраться до 103 будет непросто. - person ephemient; 05.06.2009
comment
Осмелюсь ли я соблазнить тебя сломать 100? : P Нет, правда, пожалуйста, не тратьте на это больше времени, если только вы этого не хотите (для себя)! Это отличное решение само по себе - безусловно, достаточно хорошее для меня. - person Noldorin; 06.06.2009
comment
Это больше похоже на упорный отказ поверить в то, что Perl может быть навсегда побежден его младшим кузеном Руби, но да, это не совсем продуктивное использование времени ... - person ephemient; 06.06.2009
comment
Я нашел способ сделать его короче, но он не работает. Отрицательные индексы массива в Perl переносятся один раз, но если они переносятся за конец массива, они не продолжают переноситься. В противном случае я мог бы получить его до 105. (Он тоже идеально выстроился, что меня действительно беспокоит - если бы он продолжал переноситься, элемент -42 оказался бы индексом 2, и он работал бы отлично.) - person Chris Lutz; 06.06.2009

Ассемблер

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
person Community    schedule 02.06.2009
comment
Сборка - это настоящее программирование! - person Andreas Rejbrand; 06.06.2010
comment
Однажды я видел полный тетрис в 64 байта - person BlueRaja - Danny Pflughoeft; 09.06.2010

Рубин

Количество символов: 103

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

Это нерекурсивная версия решения The Wicked Flea. Подвыражения в скобках оцениваются снизу вверх, а не сверху вниз.

Изменить: преобразование while в условную + хвостовую рекурсию сохранило несколько символов, поэтому оно больше не является нерекурсивным (хотя рекурсия не является семантически необходимой).

Изменить: заимствование идеи Дэниела Мартина о слиянии регулярных выражений позволяет сохранить еще 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
person Community    schedule 02.06.2009
comment
Я почти думал, что это новый лидер, пока не увидел, что Perl был отредактирован, чтобы стать еще короче! В любом случае, хорошая работа. - person Noldorin; 02.06.2009
comment
Избавившись от 'e = readline.chomp; ...; p e.to_f' и используя 'def q (e); ...; e.to_f; end', как и другие решения, можно сэкономить 10 символов. Однако он не может q (1 + 3 / -8) == - 0,5, как в вопросе. - person ephemient; 02.06.2009
comment
@ephemient, это обнаруженная вами ошибка - он не может обрабатывать отрицательные числа. - person finnw; 02.06.2009
comment
Gsub! ('-', '') в моем коде указывает, как работает аргумент в скобках, если он отрицен. Если результат внутренней части отрицательной скобки отрицательный, минус вне оператора остается: --7,0, например. Однако поддержка этого стоит мне 24 символа, а вам - 19. Я не знаю, могу ли я уменьшить его больше, чем уловки, которые я узнал от вас. (Но я отлично справился со второй попыткой!) - person Robert K; 03.06.2009
comment
Использование send действительно приближается к нарушению правила no eval. Но хороший трюк, включающий пробелы в регулярное выражение числа. Используя этот и еще один трюк, мое решение на Perl сократилось до 119 символов. - person Daniel Martin; 05.06.2009
comment
Perl сейчас упал до 107, но это все еще 4 + 103 ... - person ephemient; 05.06.2009

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";}
person Community    schedule 03.06.2009

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
person Community    schedule 01.06.2009
comment
Я тоже, наверное, мог бы набрать 10к символов, если бы не так поздно :) - person Jason; 01.06.2009
comment
Вы знаете, что чем меньше персонажей, тем лучше? Таким образом, они никогда не думают, что vb.net - это что-то хорошее. - person Ikke; 01.06.2009
comment
@ikke - должно было быть как можно НЕСКОЛЬКО символов? о боже ... кажется, кто-то упустил суть - person Jason; 01.06.2009
comment
ZERO_STRING_REPRESENTATION похоже на то, что принадлежит thedailywtf - person erikkallen; 13.10.2009
comment
@lkke - Думаю, в этом суть. См. Комментарий от finnw. - person phkahler; 15.02.2010
comment
+1 это рассмешило меня больше, чем любой другой ответ на SO. когда-либо. - person R.. GitHub STOP HELPING ICE; 08.10.2010

Haskell

Количество символов: 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)
person Community    schedule 30.05.2009
comment
Я подозреваю, что еще можно опуститься ниже 225, но это все, что я могу сделать в 3 часа ночи. - person ephemient; 30.05.2009
comment
Это выглядит довольно элегантным функциональным решением. (Тот, который я бы точно не понял без комментариев, так что спасибо за них.) Кроме того, в настоящий момент вы лишь немного опережаете решение Дэйва на Python, так что, похоже, вы лидируете! Мне было бы любопытно, сможет ли решение F # соответствовать этому или даже превзойти его. - person Noldorin; 30.05.2009
comment
Мне интересно, что решение Parsec (комбинаторы синтаксического анализатора = обобщенное регулярное выражение + другое), даже если была предпринята попытка минимизации, не приближается к ручному синтаксическому анализу. Я не думаю, что синтаксис F # может быть таким лаконичным, как Haskell, но я бы тоже приветствовал конкуренцию :) - person ephemient; 31.05.2009
comment
@ephemient: Я попытался быстро использовать как регулярное выражение, так и модуль синтаксического анализатора в Python. Почти сразу было ниже 300 символов, но не видел шансов стать конкурентоспособными. Проблема в импорте и вызовах функций, которые съедают слишком много. Это верно для большинства языков (за исключением Perl). Кстати, вам не нужно разбираться, чтобы получить существенно меньше 300 символов, как показывает мое решение. - person stephan; 31.05.2009
comment
Я думаю, вы слишком сильно наказываете своих персонажей. Проблема заключалась в использовании функции String- ›Double, поэтому вы должны подсчитывать символы, заменяя main = interact $ show. с q =, чтобы получить еще 17 символов, получится 209. - person Daniel Martin; 01.06.2009

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
person Community    schedule 30.05.2009
comment
Примечание: текущая версия не обрабатывает ввод вроде - (1.0), хотя корректно обрабатывает отрицательные литералы. Из спецификации не было ясно, требуется ли это. - person Dave; 30.05.2009
comment
Можно бесплатно сделать l неглобальным, поместив его в список параметров e. Однако по-прежнему не будет потокобезопасным. - person Dave; 30.05.2009
comment
Очень хитро. Это стоило усилий по интерпретации. :) - person Nick Johnson; 30.05.2009
comment
@ Дэйв: Моя тоже терпит неудачу на -(1.0), так что не беспокойтесь! Уточню вопрос. В любом случае, это кажется очень умным решением - хотя я все еще пытаюсь понять, как оно работает (точно не зная Python). Если бы вы могли добавить краткое объяснение, я был бы очень признателен. - person Noldorin; 30.05.2009

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, длинные и вездесущие ключевые слова и простые примитивы.

person Community    schedule 31.05.2009
comment
Боже правый, мужик! Тебе, должно быть, сегодня было скучно. ;) - person gnovice; 31.05.2009
comment
Хе-хе, не думаю, что когда-либо ожидал решения на Фортране! Я думаю, мы можем сделать вывод, что этот язык не особенно хорошо подходит для программирования гольфа? В любом случае проголосовали за старания и использование устаревшего языка. :) - person Noldorin; 31.05.2009
comment
Я нахожу этот вид неудобной игры с байтами многословным и неудобным на фортране, но на самом деле не сложным. С другой стороны, писать неструктурированный код и использовать эти вычисленные ветви довольно странно. - person dmckee --- ex-moderator kitten; 01.06.2009
comment
Отлично, но как версия fortran с символом 2000+ набирает больше голосов, чем моя короткая маленькая версия ruby1.9? ржу не могу - person Daniel Huckstep; 05.06.2009
comment
@darkhelmet: Понятия не имею. Я сделал это на шутку и ожидал одного или двух голосов за старания и извращенность. Я до неприличия горжусь этой мерзостью, а это смешно ... - person dmckee --- ex-moderator kitten; 05.06.2009

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

Функция не повторяется.

РЕДАКТИРОВАТЬ от Криса Лутца: я ненавижу попирать чужой код, но вот версия с символом 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(), но это никогда не делает его короче. Я все еще верю, что это возможно, но я покончил с гольфом. Если я и дальше буду работать над этим вопросом, он будет на языке, который нельзя называть.

person Community    schedule 30.05.2009
comment
Хорошее решение и бонусные баллы за использование чистого C. Также на 3 символа превосходит мою! Повторное вхождение не входило в правила, так что это нормально. (Однако это плюс.) - person Noldorin; 30.05.2009
comment
Хороший! Вы можете сократить еще несколько символов, используя коды ASCII, например замените «0» на 48 и т.д. проблема. - person Adam Rosenfield; 30.05.2009
comment
Я думал об использовании atof (), но он не сообщает вам, где заканчивается строка с плавающей запятой, поэтому вам все равно нужно будет ее проанализировать. - person Ferruccio; 30.05.2009
comment
Спасибо за подсказку, Адам. Воспользовавшись этим и парой других (уродливых) уловок, я немного уменьшил его. - person Ferruccio; 30.05.2009
comment
Ой, я не рассчитывал на отрицательные числа. Код увеличен до 400 знаков. - person Ferruccio; 30.05.2009
comment
О верно; Я думаю, вы не можете использовать atof (), но вы можете использовать strtod () или sscanf () с модификатором% n. - person Adam Rosenfield; 30.05.2009
comment
Еще одна микрооптимизация: замените o == 0 || o == 41 на! O | o == 41 - person Adam Rosenfield; 30.05.2009
comment
Хороший совет по поводу strtod (), я даже не знал об этом. Он немного урезал его, несмотря на то, что ему нужен #include. - person Ferruccio; 30.05.2009
comment
Вы можете сохранить два символа, используя тот факт, что strtod () пропускает ведущие пробелы. - person Dave; 31.05.2009
comment
Не совсем, Дэйв, пробелы все равно нужно пропускать в том случае, если есть (а не число. - person Ferruccio; 31.05.2009
comment
Дох! Мне больше не нужен макрос W! - person Ferruccio; 31.05.2009
comment
Еще одна поправка: замените #include объявлением exlpicit double strtod () ;. В C (но НЕ C ++) пустой набор скобок означает, что функция принимает любое количество параметров любого типа. К сожалению, объявление нельзя полностью исключить, потому что оно не возвращает int. - person Adam Rosenfield; 31.05.2009
comment
На самом деле, прикрутите strtod () и используйте вместо него strtof () (только для C99)! Сохраняет еще один символ (если вы объявляете его самостоятельно вместо включения stdlib.h) и избавляется от предупреждения о двойном значении числа с плавающей запятой. - person Adam Rosenfield; 31.05.2009
comment
Убрано еще несколько символов, сделав op float вместо char. Меня немного тошнило, но это сработало. - person Ferruccio; 31.05.2009
comment
Молодец за настойчивость! Я не уверен, что вы поймаете решения Haskell / Python, но вы удивительно близки, гораздо больше, чем я когда-либо думал, что решение на C может быть получено. Что интересно, я считаю, что все это решение можно преобразовать в C # с минимальными затратами на использование символов. - person Noldorin; 31.05.2009
comment
Вы можете сохранить еще несколько символов, используя то, что я использовал в своем решении на Perl - последняя восьмеричная цифра кодов символов различается для каждого символа оператора, поэтому {{F o = * e ++; if (! O | o == 41 ) ...}} эквивалентно {{F o = 7 & * e ++; if (o ‹2) ...}}. Вы должны иметь возможность выжать еще несколько символов, используя ‹и однозначные цифры в оставшейся части этой длинной строки; просто отсортируйте по коду символа оператора. - person Daniel Martin; 31.05.2009
comment
@Nosredna: Я подумал об этом, но на самом деле это привело к увеличению размера на пару символов. Еще один возврат, и это сработало бы. - person Ferruccio; 31.05.2009
comment
После дня игры в гольф у меня один из них снизился до 214, но я собираюсь посмотреть, смогу ли я побриться еще, прежде чем опубликовать его (я собираюсь сломать 200, но сомневаюсь, что это произойдет) . Подсказки к тому, что я сделал: g () и f () достаточно просты для встраивания, strtof () заменяет вам начальные пробелы, o-41 то же самое, что o! = 41, но короче. Я скоро отправлю код, если больше не смогу бриться. - person Chris Lutz; 04.06.2009
comment
209, и я выкладываю это и освобождаюсь от этого. Следует ли мне отредактировать этот пост и добавить свое решение или создать новое? - person Chris Lutz; 04.06.2009
comment
не стесняйтесь редактировать этот пост, если хотите - person Ferruccio; 04.06.2009
comment
+ Это бунт. Это похоже на то, что я делаю при настройке производительности большого программного обеспечения, когда у меня есть такая возможность. Я не запутываю, но люблю отбрасывать важные факторы. - person Mike Dunlavey; 04.06.2009
comment
Woohoo! Неуклюжий старый C теперь превосходит все решения Python, а также Haskell! - person Chris Lutz; 04.06.2009
comment
Препроцессор позволяет сделать это, а затем использовать D для #define? Если так, я думаю, я могу вырезать еще пару персонажей. --- #define D #define - person Nosredna; 05.06.2009
comment
@Nosredna - Нет, это не так. Он проверяет расширения макросов для других макросов, но не для новых директив препроцессора. - person Chris Lutz; 05.06.2009

Common 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(), которая принимает один строковый аргумент. Он использует уловку, заключающуюся в том, что числа / операнды и операторы находятся в шаблоне N O N O N ... и рекурсивно оценивает все операнды, поэтому вложение получается очень дешевым. ;)

person Community    schedule 30.05.2009
comment
Умное решение. Тем не менее, я не совсем уверен, что это полностью верно, учитывая, что спецификация была для функции, принимающей строковый объект. - person Noldorin; 30.05.2009
comment
Извини за это. Фиксированный! - person ; 30.05.2009
comment
Без проблем. Не понимал, что преобразование было таким простым. Хорошее решение, тем не менее! - person Noldorin; 30.05.2009
comment
Ух ты. Это прекрасно. :) - person Emil H; 19.06.2009

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 условным оператором.

person Community    schedule 30.05.2009
comment
Это очень хорошо. Я ждал, что здесь кто-нибудь воспользуется регулярным выражением. :) Казалось бы, у динамических языков определенно есть преимущество в этой проблеме. - person Noldorin; 30.05.2009

C # с Regex Love

Количество символов: 384

Полностью запутанный:

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

Не запутанный:

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

    float result = 0;

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

    return result;
}

Использует функцию балансировки группы .NET Regex.

person Community    schedule 30.05.2009
comment
Спасибо за это решение. :) Я не был уверен, увижу ли я решение C # с регулярным выражением, но вот оно у нас. Теперь спорный вопрос, следует ли включать using System.Text.RegularExpressions; в вашем подсчете символов, но тем не менее это хорошее решение. - person Noldorin; 30.05.2009
comment
Это не входило в правила :). Если вы добавите using R = System.Text.RegularExpressions.Regex; и замените мое Regex на R, он перейдет к 417. - person Jeff Moser; 30.05.2009
comment
@Jeff: Ну, технически он не будет компилироваться без оператора using, поэтому по умолчанию он должен быть включен. Однако мелочь, учитывая, что все наши решения C # значительно отстают от лидера. - person Noldorin; 31.05.2009

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)

Должен работать с любым допустимым вводом (включая отрицательные числа и произвольные пробелы)

person Community    schedule 30.05.2009
comment
preg_replace() с модификатором e сэкономит вам еще несколько байтов. - person Alix Axel; 04.05.2012

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. Добавить новых операторов несложно. Изменить приоритет операторов несложно.

person Community    schedule 11.06.2009
comment
Черт возьми, не думаю, что когда-либо ожидал решения SQL! Это не совсем в духе кодового гольфа, но все равно было одобрено за дерзость (и даже без использования языка программирования). :) - person Noldorin; 11.06.2009
comment
@Noldorin, а почему это не в духе кодового гольфа? - person tuinstoel; 25.09.2009

F#

Количество символов: 327

OP искал версию F #, вот она. Это может быть сделано намного лучше, поскольку я злоупотребляю здесь ref для сохранения персонажей. Он обрабатывает большинство вещей, таких как - (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)
person Community    schedule 30.05.2009
comment
В самом деле, я надеялся на решение на F #. Спасибо за это. Количество символов тоже довольно приличное, особенно учитывая, что System.Text.RegularExpressions.Regex.Matches занимает абсурдное количество символов. - person Noldorin; 30.05.2009
comment
да, то же самое с вызовами .Value.IsEmpty / Tail / Head - у меня есть новая версия в разработке; p в надежде на менее 250 символов. - person thr; 30.05.2009
comment
На самом деле я не уверен, разрешено ли вам в некоторых соревнованиях по гольфу импортировать / использовать операторы вне счетчика символов. Если так, это определенно поможет. :) Жду новой версии. - person Noldorin; 30.05.2009
comment
@Noldorin: Нет, извините, я не могу получить его под 327 символами этого (немного улучшенного с момента последнего) кода. Выигрыш от того, что все идеально проанализировано с помощью регулярного выражения, перевешивает безумно длинное имя System.Text.RegularExpressions.Regex.Matches Если бы у F # было короткое (с псевдонимом) имя для функции совпадений, я бы имел 288 символов, но он не делает = /. - person thr; 30.05.2009
comment
@fredrikholmstrom: Не беспокойтесь - тем не менее, хорошее решение. Кроме того, я не совсем уверен, но я бы сказал, что вы можете переместить System.Text.RegularExpressions в открытый оператор и, по крайней мере, исключить счетчик символов для этого. - person Noldorin; 31.05.2009

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 в x%y и _z у J. Без этого, возможно, 50% этого кода могло бы исчезнуть.

person Community    schedule 01.06.2009
comment
Да, это очень мило. А что насчет решения в K? : P Я подозреваю, что это могло бы даже быть лучше Perl. - person Noldorin; 01.06.2009
comment
Woohoo, мне удалось получить свое решение Haskell под моим решением J! Хотя, если бы здесь кто-то был волшебником J, K или APL, он, вероятно, разрушил бы 200-символьный барьер ... - person ephemient; 06.06.2009

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
person Community    schedule 01.06.2009
comment
+1 Хорошая идея для диктовки. Однако текущая версия не работает на e ('1 + 0'). Вместо этого используйте {'+': n + m, '-': n-m, '': n m, '/': n / m, если m else 1}. Я позаимствовал вашу идею (с этой поправкой). Спасибо - person stephan; 01.06.2009
comment
Спасибо. Я не думал о проблеме DivZero; исправление из 7 символов - n / (m или 1). - person krubo; 01.06.2009
comment
Сделаю это и для моей программы ;-) - person stephan; 01.06.2009
comment
хе-хе, сейчас ничего не меняйте, кол-во символов красивое :) - person Tetha; 02.06.2009

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

person Community    schedule 30.05.2009
comment
Любые советы о том, как я могу уменьшить количество символов, будут приветствоваться. (Это моя первая настоящая попытка кодового гольфа.) - person Noldorin; 30.05.2009
comment
Я получил <400, но не прошел отредактированный тест, который вы добавили ;-p - person Marc Gravell; 31.05.2009
comment
Предложения: var для float, char - только немного бреет и, тем не менее, теряет совместимость с C # 1.2 / 2.0. - person Marc Gravell; 31.05.2009
comment
@Marc: Да, я тоже. С некоторыми другими незначительными изменениями я мог бы снизить его до 390, но не меньше. - person Noldorin; 31.05.2009
comment
Красивое решение Нолорин. Мне удалось сократить ваше решение до 361 - person Crispy; 02.06.2009
comment
@Chris: Спасибо - хотя теперь мне любопытно, как тебе удалось так быстро сократить счетчик символов. (Я мог бы, наверное, скинуть еще около 15 или 20, но не больше.) Не могли бы поделиться этим с нами? Не стесняйтесь редактировать даже мой пост. - person Noldorin; 02.06.2009
comment
Я сократил его до 294 символов, используя немного регулярного выражения. :) См. Мой новый ответ: http://stackoverflow.com/questions/928563/code-golf-evaluating-matMathematical-expressions/944716#944716 - person Noldorin; 03.06.2009
comment
Не работает для унарного минуса: (5 + 5) * (3 + 2) -1 Оценивается как 51. - person Sheed; 12.02.2010

А вот еще один:

Сценарий оболочки (с использованием 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
person Community    schedule 01.06.2009
comment
Я (почти) не знаю, как это работает, но меня поражает, насколько хорошо сценарий оболочки справляется с этой задачей! Действительно молодцы. - person Noldorin; 01.06.2009
comment
Что ж, просто помните, что многие операционные системы используют этот микс языка / инструментов для множества разных задач :) - person soulmerge; 01.06.2009

MATLAB (версия 7.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

Функция 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

Контрольная работа:

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

person Community    schedule 01.06.2009

Рубин

Количество символов: 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 (без eval).

Изменить: я все еще гонюсь за Perl, но сейчас это второй самый маленький ответ. Я уменьшил его, изменив второе регулярное выражение и изменив обработку строки на деструктивную (заменяя старую строку). Это устранило необходимость дублировать строку, которая, как я выяснил, была просто новым указателем на строку. И переименование функции в s из resolve позволило сэкономить несколько символов.

person Community    schedule 31.05.2009
comment
Хорошая работа, удивлен, что я сам не пробовал этот подход, так как я использовал что-то очень похожее для решения другого вопроса синтаксического анализа. - person Mike Tunnicliffe; 02.06.2009
comment
См. Мое решение, чтобы узнать, как сжать это регулярное выражение. Последняя «полоска» тоже не понадобится. И не похоже, что вы полностью реализуете унарный минус, поэтому вы получаете небольшую выгоду от gsub ('-', ''). - person finnw; 02.06.2009
comment
На самом деле я не могу сократить свой конкретный алгоритм или провалю 3-4 теста, я не уверен, почему. Я мог бы уменьшить его, может быть, на 20 символов. - person Robert K; 03.06.2009

Python с регулярными выражениями

Количество символов: 283

Полностью запутанная функция:

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

Не запутывается:

import re
from operator import *

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

Я хотел посмотреть, смогу ли я превзойти другие решения Python, использующие регулярные выражения.

Не удалось.

Регулярное выражение, которое я использую, создает список пар (val, op), в котором действителен только один элемент в каждой паре. Остальная часть кода представляет собой довольно стандартный синтаксический анализатор на основе стека с изящным приемом замены трех верхних ячеек в стеке результатом вычисления с использованием синтаксиса назначения списка Python. Для работы с отрицательными числами потребовалось всего два дополнительных символа (-? В регулярном выражении).

person Community    schedule 30.05.2009
comment
Вы можете сэкономить пару байтов, удалив () из строки оператора; zip останавливается в конце более короткого списка. - person Ben Blank; 01.06.2009
comment
@gooli: Вы используете Windows? По моим подсчетам, опубликованное решение составляет всего 273. Одно из объяснений этого может заключаться в том, что вы посчитали символы новой строки как два каждый. (Python не заботится о том, есть ли у вас новые строки с одним символом, даже в Windows.) Другое объяснение - это нажатие 8, когда вы имели в виду 7.;) - person John Y; 02.06.2009

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

У меня возникла эта идея, когда я сдавался, и я не мог отказаться от нее, пока не записал ее и не заставил ее работать.

person Community    schedule 30.05.2009
comment
Хорошее решение ... Мне тоже кажется очень понятным. Казалось бы, использование dict / zip для хранения операторов определенно является очень эффективным подходом в Python. - person Noldorin; 31.05.2009

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));
}
person Community    schedule 30.05.2009
comment
Прекрасно, решение на C #. В частности, весьма интересно использование вами типов, допускающих значение NULL. 484 выглядит неплохо, учитывая, что у вас не было времени на то, чтобы привести его в порядок. (Я полагаю, что одним из улучшений было бы преобразование оператора switch в серию if.) Я опубликовал свое собственное решение C # сейчас, если вы хотите сравнить. :) - person Noldorin; 30.05.2009

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. Явно злоупотребляя принципом try-except, я использую метод проб и ошибок. Он должен правильно обрабатывать все случаи, включая такие вещи, как -(8), --8 и g('-(1 - 3)'). Это повторно въезжающий. Без поддержки случая --, который не поддерживается многими реализациями, он состоит из 217 символов (см. Предыдущую версию).

Спасибо за интересный час в воскресенье и еще 30 минут в понедельник. Спасибо krubo за его прекрасный изречение.

person Community    schedule 31.05.2009
comment
Еще один интересный подход ... Идентичный по длине и одному из других решений Python. Это подтверждает мое мнение о том, что использование словаря операторов - лучший способ, где это возможно. Я хотел сделать что-то подобное на C #, но синтаксис просто занимает слишком много символов. - person Noldorin; 31.05.2009

Рубин

Количество символов: 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
person Community    schedule 01.06.2009
comment
На самом деле мой короче (198) и использует регулярное выражение для решения скобок сверху вниз перед окончательным результатом математики. Итак, 3 + (3 * (3 + 9)) идет: 3 + (3 * 12), 3 + 36, 39. Оно идет сверху вниз, слева направо. Он решает все тесты, кроме одной незначительной ошибки, которая требует пробелов между токенами. См .: stackoverflow.com/questions/928563 / - person Robert K; 02.06.2009
comment
Не то чтобы ваш не умный, но очень умный. - person Robert K; 02.06.2009
comment
(3 + 7) - (5 + 2) - это то, что я имел в виду под несколькими группами скобок. В вашем решении есть проблема с невложенными круглыми скобками из-за жадности регулярных выражений. - person samuil; 02.06.2009
comment
Может быть, но я вчера вечером возился со своим парсером и улучшил его в своей системе (с математическими функциями и однобуквенными переменными). Итак, я вытащил из него свое лучшее регулярное выражение, и оно работает нормально, сообщение также обновлено с новым счетчиком символов. ;-) Я на мгновение добавлю ваше уравнение к тестам в ответе. - person Robert K; 02.06.2009
comment
Я не думаю, что использование «метода» или «отправки» является обманом - это просто поиск по таблице - вы не используете встроенный анализатор. - person finnw; 02.06.2009

Рубин 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
person Community    schedule 30.05.2009
comment
Это неплохо для первой попытки, да и длина в любом случае не сильно отличается от других. Конечно, алгоритм довольно понятен. Обратите внимание, что вы можете значительно уменьшить количество символов, просто используя однобуквенные имена переменных! - person Noldorin; 30.05.2009
comment
Спасибо. Моя последняя ошибка потребовала времени, чтобы исправить, но в целом это не было чем-то ломающим; к счастью, он функционирует полностью. - person Robert K; 30.05.2009

Рубин 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

РЕДАКТИРОВАТЬ: включает оптимизацию Мартина.

person Community    schedule 30.05.2009
comment
r = m [1] .to_f.send (m [3], m [4] .to_f), если% w {+ - * /}.include?m[3] - person Martin Carpenter; 01.06.2009
comment
Даже лучше! Я пытался придумать хороший способ сделать это, но это не пришло мне в голову. - person Daniel Huckstep; 01.06.2009

СНОБОЛ4

Количество символов: 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]
person Community    schedule 02.06.2009

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, удалив один из возвращаемых статусов.

person Community    schedule 02.06.2009
comment
Ах, я не знал, что вы уже опубликовали его как новый ответ. Спасибо за все заслуги (что, вероятно, больше, чем я заслуживаю, так как я застрял около 390). Вскоре я более внимательно рассмотрю модификации ... единственное, что я подумал, - это изменение сравнения символов для использования чисел. :) - person Noldorin; 03.06.2009

C # с регулярным выражением

Количество символов: 294

Это частично основано на ответе Джеффа Мозера, но со значительно упрощенной техникой оценки. Могут быть даже другие способы уменьшить количество символов, но теперь я очень рад, что есть решение C # до 300 символов!

Полностью обфусцированный код:

float e(string x){while(x.Contains("("))x=Regex.Replace(x,@"\(([^\(]*?)\)",m=>e(m.Groups[1].Value).ToString());float r=0;foreach(Match m in Regex.Matches("+"+x,@"\D ?-?[\d.]+")){var o=m.Value[0];var v=float.Parse(m.Value.Substring(1));r=o=='+'?r+v:o=='-'?r-v:o=='*'?r*v:r/v;}return r;}

Более четкий код:

float e(string x)
{
    while (x.Contains("("))
        x = Regex.Replace(x, @"\(([^\(]*?)\)", m => e(m.Groups[1].Value).ToString());
    float r = 0;
    foreach (Match m in Regex.Matches("+" + x, @"\D ?-?[\d.]+"))
    {
        var o = m.Value[0];
        var v = float.Parse(m.Value.Substring(1));
        r = o == '+' ? r + v : o == '-' ? r - v : o == '*' ? r * v : r / v;
    }
    return r;
}
person Community    schedule 03.06.2009

Python

Количество символов: 492

Слегка запутанная функция (короткие имена переменных, без пробелов вокруг операторов):

def e(s):
    q=[]
    b=1
    v=[]
    for c in s.replace(' ','')+'$':
        if c in '.0123456789' or c in '+-' and b and not v:
            v+=[c]
        else:
            if v:
                q+=[float(''.join(v))]
                v=[]
            while len(q)>=3:
                x,y,z=q[-3:]
                if type(x)==type(z)==float:
                    if y=='+':q[-3:]=[x+z]
                    elif y=='-':q[-3:]=[x-z]
                    elif y=='*':q[-3:]=[x*z]
                    elif y=='/':q[-3:]=[x/z]
                elif (x,z)==('(',')'):q[-3:]=[y]
                else:break
            if c=='$':break
            q+=[c]
            b=c!=')'
    return q[0]

Я думаю, это относительно легко понять. Это довольно простой и наивный подход. Он ничего не импортирует, не использует регулярное выражение, полностью самодостаточен (одна функция, без глобальных переменных, без побочных эффектов) и должен обрабатывать подписанные литералы (положительные или отрицательные). Использование более разумных имен переменных и соблюдение рекомендованного форматирования Python увеличивает количество символов до 850-900, большая часть этого показателя заключается в использовании четырех пробелов вместо одной табуляции для отступа.

person Community    schedule 30.05.2009
comment
Включает ли это количество символов все пробелы? Если это так, я уверен, что вы можете значительно уменьшить его. Ну, код в любом случае довольно ясен, так что хорошо с этим справились. Это также бонус в том, что он повторно участвует. - person Noldorin; 30.05.2009
comment
Мое количество символов было тем, что редактор сказал мне, это размер выделения от 'd' в def до ']' в конце возврата. Таким образом, это включает один символ (табуляция для удобства чтения) для каждого уровня отступа. Этот отступ необходим для Python, поэтому его нужно учитывать. Спасибо за комментарий; Я знал, что никогда не буду соревноваться на чистом подсчете персонажей или изобретательности. - person John Y; 30.05.2009
comment
Да, достаточно честно. Я только что заметил, что другой плакат с решением Python ушел с использованием одного пробела для отступа, но если вы используете вкладки, то это эквивалент. - person Noldorin; 30.05.2009
comment
Я только что заметил пердение в моем коде. Почему я сделал v списком, а не просто строкой? Строка не только сохраняет 11 символов, но и немного понятнее, так как это то, что мне в любом случае понадобится (Python '' .join () не самый интуитивно понятный). - person John Y; 30.05.2009

Python 3K

(его 3К, потому что / преобразует результат в число с плавающей запятой)

Количество символов: 808

Ясно (я не могу писать запутанный код в Python XD):

def parse(line):
  ops = {"+": lambda x,y:x+y,
       "-": lambda x,y:x-y,
       "*": lambda x,y:x*y,
       "/": lambda x,y:x/y}
  def tpp(s, t):
    if len(s) > 0 and s[-1] in ops:
      f = ops[s.pop()]
      t = f(s.pop(), t)
    return t
  line = line + " "
  s = []
  t = 0
  m = None
  for c in line:
    if c in "0123456789":
      if not m:
        m = "i"
      if m == "i":
        t = t*10 + ord(c)-ord("0")
      elif m =="d":
        t = t + e*(ord(c)-ord("0"))
        e*=0.1
    elif c == ".":
      m = "d"
      e = 0.1
    elif m:
      t = tpp(s,t)
      s.append(t)
      m = None
      t = 0

    if c in ops or c == "(":
      s.append(c)
    elif c == ")":
      t = s.pop()
      s.pop()
      s.append(tpp(s,t))
      t = 0
  t = s.pop()
  if int(t) == t:
    t = int(t)
  return t

Я не использую никаких регулярных выражений, даже парсинг числа производится вручную ;-)

Довольно просто, сканирует строку, это может быть в 3 разных режимах (m), None означает, что нет анализируемого числа, «i» означает, что анализируется целая часть, а «d» означает, что анализируется десятичная часть.

Он использует стек для хранения временных вычислений, когда он завершает синтаксический анализ числа, видит, был ли в стеке оператор, в этом случае вычисляет и выталкивает. Открывающие скобки просто вставляются, а закрывающие скобки удаляют открывающую скобку и повторно вставляют текущий eval.

Довольно просто и понятно :-)

person Community    schedule 30.05.2009

Рубин

Количество символов: 302

Полуобфусцированный:

def e(l)
  t=0.0;o=nil
  while l!=''
    l.sub!(/^\s+/,'')
    l.sub!(/^(-?\d+|-?\d+\.\d+)/,'')
    t=o ? t.send(o, $1.to_f) : $1.to_f if $~
    l.sub!(/^(\+|-|\*|\/)/,'')
    o=$1 if $~
    l.sub!(/^\(/,'')
    t=o ? t.send(o, e(l)) : e(l) if $~
    l.sub!(/^\)/,'')
    return t if $~
  end
  t
end

Уничтожает исходную строку, также предполагает, что выражение правильно сформировано (только допустимые символы и соответствующие скобки).

Без запутывания:

def evaluate_expression(expression)
  result_so_far = 0.0
  last_operator = nil

  while (expression != '')
    # remove any leading whitespace
    expression.sub!(/^\s+/, '') 

    # extract and remove leading integer or decimal number
    expression.sub!(/^(-?\d+|-?\d+\.\d+)/, '')
    if $~
      # match was successful
      number = $1.to_f
      if last_operator.nil?
        # first number, just store it
        result_so_far = number
      else
        # we have an operator, use it!
        # last_operator is a string matching '+', '-', '*' or '/'
        # just invoke the method of that name on our result_so_far
        # since these operators are just method calls in Ruby
        result_so_far = result_so_far.send(last_operator, number)
       end
    end

    # extract and remove leading operator +-*/
    expression.sub!(/^(\+|-|\*|\/)/, '')
    if $~
      # match was successful
      last_operator = $1
    end

    # extract and remove leading open bracket
    l.sub!(/^\(/, '')
    if $~
      # match successful
      if last_operator.nil?
        # first element in the expression is an open bracket
        # so just evaluate its contents recursively
        result_so_far = evaluate_expression(expression)
      else
        # combine the content of the bracketing with the
        # result so far using the last_operator
        result_so_far.send(last_operator, evaluate_expression(expression))
      end
    end

    # extract and remove leading close bracket
    l.sub!(/^\)/, '')
    if $~
      # match successful
      # this must be the end of a recursive call so
      # return the result so far without consuming the rest
      # of the expression
      return result_so_far
    end
  end
  t
end

Рекурсивный вызов контролируется модификацией строки выражения, что немного неприятно, но, похоже, работает.

person Community    schedule 30.05.2009
comment
Не могли бы вы также поставить читаемую версию? Я не совсем понимаю, как у вас работает. - person Robert K; 30.05.2009

F#

Количество символов: 461

Вот решение Марка Гравелла (по сути) преобразованное с C # на F #. Счетчик символов немного лучше, но я решил, что все равно выложу его из интереса.

Обфусцированный код:

let e x=
 let rec f(s:string)=
  let i=s.IndexOf(')')
  if i>0 then
   let j=s.LastIndexOf('(',i)
   f(s.Substring(0,j)+f(s.Substring(j+1,i-j-1))+s.Substring(i+1))
  else
   let o=[|'+';'-';'*';'/'|]
   let i=s.LastIndexOfAny(o)
   let j=s.IndexOfAny(o,max(i-2)0,2)
   let k=if j<0 then i else j
   if k<0 then s else
    let o=s.[k]
    string((if o='+'then(+)else if o='-'then(-)else if o='*'then(*)else(/))(float(f(s.Substring(0,k))))(float(s.Substring(k+1))))
 float(f x)
person Community    schedule 30.05.2009
comment
Да, вчера я пробовал что-то подобное с F #, но он и выглядит совсем не F #, и в нем все еще больше символов, чем в другом моем решении; (. Глупо, что F # так ужасно обрабатывает строки. - person thr; 31.05.2009

Ява

Количество символов: 376

Обновленная версия, теперь еще? злоупотребление оператором!

Полностью запутанное решение:

static double e(String t){t="("+t+")";for(String s:new String[]{"+","-","*","/","(",")"})t=t.replace(s," "+s+" ");return f(new Scanner(t));}static double f(Scanner s){s.next();double a,v=s.hasNextDouble()?s.nextDouble():f(s);while(s.hasNext("[^)]")){char o=s.next().charAt(0);a=s.hasNextDouble()?s.nextDouble():f(s);v=o=='+'?v+a:o=='-'?v-a:o=='*'?v*a:v/a;}s.next();return v;}

Очистить / полуобфусцированную функцию:

static double evaluate(String text) {
    text = "(" + text + ")";
    for (String s : new String[] {"+", "-", "*", "/", "(", ")" }) {
        text = text.replace(s, " " + s + " ");
    }
    return innerEval(new Scanner(text));
}

static double innerEval(Scanner s) {
    s.next();
    double arg, val = s.hasNextDouble() ? s.nextDouble() : innerEval(s);
    while (s.hasNext("[^)]")) {
        char op = s.next().charAt(0);
        arg = s.hasNextDouble() ? s.nextDouble() : innerEval(s);
        val =
            op == '+' ? val + arg :
            op == '-' ? val - arg :
            op == '*' ? val * arg :
            val / arg;
    }
    s.next();
    return val;
}
person Community    schedule 01.06.2009

C++

Символов: 1670

 // not trying to be terse here
#define DIGIT(c)((c)>='0' && (c) <= '9')
#define WHITE(pc) while(*pc == ' ') pc++
#define LP '('
#define RP ')'

bool SeeNum(const char* &pc, float& fNum){
    WHITE(pc);
    if (!(DIGIT(*pc) || (*pc=='.'&& DIGIT(pc[1])))) return false;
    const char* pc0 = pc;
    while(DIGIT(*pc)) pc++;
    if (*pc == '.'){
        pc++;
        while(DIGIT(*pc)) pc++;
    }
    char buf[200];
    int len = pc - pc0;
    strncpy(buf, pc0, len); buf[len] = 0;
    fNum = atof(buf);
    return true;
}

bool SeeChar(const char* &pc, char c){
    WHITE(pc);
    if (*pc != c) return false;
    pc++;
    return true;
}

void ParsExpr(const char* &pc, float &fNum);

void ParsPrim(const char* &pc, float &fNum){
    if (SeeNum(pc, fNum));
    else if (SeeChar(pc, LP)){
        ParsExpr(pc, fNum);
        if (!SeeChar(pc, RP)) exit(0);
    }
    else exit(0); // you can abort better than this
}

void ParsUnary(const char* &pc, float &fNum){
    if (SeeChar(pc, '-')){
        pc+;
        ParsUnary(pc, fNum);
        fNum = -fNum;
    }
    else {
        ParsPrim(pc, fNum);
    }
}

void ParsExpr(const char* &pc, float &fNum){
    ParsUnary(pc, fNum);
    float f1 = 0;
    while(true){
        if (SeeChar(pc, '+')){
            ParsUnary(pc, f1);
            fNum += f1;
        }
        else if (SeeChar(pc, '-')){
            ParsUnary(pc, f1);
            fNum -= f1;
        }
        else if (SeeChar(pc, '*')){
            ParsUnary(pc, f1);
            fNum *= f1;
        }
        else if (SeeChar(pc, '/')){
            ParsUnary(pc, f1);
            fNum /= f1;
        }
        else break;
    }
}

Это просто LL1 (рекурсивный спуск). Мне нравится делать это таким образом (хотя я использую двойные), потому что это достаточно быстро и легко вставлять подпрограммы для обработки уровней приоритета.

person Community    schedule 02.06.2009

PowerBASIC

Количество символов: ~ 400

Немного некрасиво, но работает. :) Я уверен, что регулярное выражение сделало бы его еще меньше.

DEFDBL E,f,i,z,q,a,v,o  
DEFSTR s,c,k,p

FUNCTION E(s)  

    i=LEN(s)  
    DO  
        IF MID$(s,i,1)="("THEN  
            q=INSTR(i,s,")")  
            s=LEFT$(s,i-1)+STR$(E(MID$(s,i+1,q-i-1)))+MID$(s,q+1)  
        END IF  
        i-=1  
    LOOP UNTIL i=0  

    k="+-*/"  
    DIM p(PARSECOUNT(s,ANY k))  
    PARSE s,p(),ANY k  

    a=VAL(p(0))

    FOR i=1TO LEN(s)
        c=MID$(s,i,1)
        q=INSTR(k,c)
        IF q THEN
            z+=1
            IF o=0 THEN o=q ELSE p(z)=c+p(z)
            IF TRIM$(p(z))<>"" THEN
                v=VAL(p(z))
                a=CHOOSE(o,a+v,a-v,a*v,a/v)
                o=0
            END IF
        END IF
    NEXT

    E=a  
END FUNCTION  
person Community    schedule 05.06.2009

C #, 264 символа

Стратегия: первые 2 строки избавляются от скобок по индукции. Затем я разделил на \-?[\d.]+, чтобы получить числа и операторы. затем с помощью агрегата уменьшить массив строк до двойного значения.

Пояснения к переменным

m - выражение в скобках без вложенных скобок.
d заменяет этот неудобный синтаксис TryParse.
v - аккумулятор для окончательного значения
t - текущий токен.

float E(string s){var d=999f;while(d-->1)s=Regex.Replace(s,@"(([^(]?))",m=>E(m.Groups[1].Value)+"");return Regex.Split(s,@"(-?[\d.]+)").Aggregate(d,(v,t)=>(t=t.Trim()).Length==0?v:!float.TryParse(t,out d)?(s=t)==""?0:v:s=="/"?v/d:s=="-"?v-d:s==""?v*d:v+d);}

    float F(string s) {
        var d=999f;
        while(d-->1)
            s=Regex.Replace(s,@"\(([^\(]*?)\)",m=>F(m.Groups[1].Value)+"");
        return Regex.Split(s, @"(\-?[\d\.]+)")
            .Aggregate(d, (v, t) => 
                (t=t.Trim()).Length == 0 ? v :
                !float.TryParse(t, out d) ? (s=t) == "" ? 0 : v :
                s == "/" ? v / d :
                s == "-" ? v - d :
                s == "*" ? v * d :
                           v + d);
    }

РЕДАКТИРОВАТЬ: бессовестно украл части из ответа noldorin, повторно использовал s в качестве переменной оператора.

РЕДАКТИРОВАТЬ: 999 вложенных скобок должно хватить всем.

person Community    schedule 09.06.2009
comment
Довольно мило. На самом деле выглядит довольно похоже на мое решение с регулярным выражением. Все это показало мне несколько интересных приемов для уменьшения количества, например +"" вместо ToString. Молодцы, что добавили LINQ. - person Noldorin; 10.06.2009
comment
вау, я не видел этого :) Возможно, я бы не стал публиковать этот ответ, если бы видел. Я думаю, что многие различия просто связаны с моим ограниченным пониманием регулярных выражений. - person Jimmy; 10.06.2009
comment
Хе-хе, не беспокойся. Я просто очень удивлен, насколько похожи многие части нашего решения. Вы, конечно, соблазнили меня уменьшить количество знаков ... Я знаю, что могу немного. - person Noldorin; 10.06.2009

OCaml напрямую использует Camlp4:

open Camlp4.PreCast

let expr = Gram.Entry.mk "expr"

EXTEND Gram
  expr:
  [   [ e1 = expr; "+"; e2 = expr -> e1 + e2
      | e1 = expr; "-"; e2 = expr -> e1 - e2 ]
  |   [ e1 = expr; "*"; e2 = expr -> e1 * e2
      | e1 = expr; "/"; e2 = expr -> e1 / e2 ]
  |   [ n = INT -> int_of_string n
      | "("; e = expr; ")" -> e ]   ];
END

let () = Gram.parse expr Loc.ghost (Stream.of_string "1-2+3*4")

OCaml с использованием расширения парсера потока Camlp4:

open Genlex

let lex = make_lexer ["+"; "-"; "*"; "/"; "("; ")"]

let rec parse_atom = parser
  | [< 'Int n >] -> n
  | [< 'Kwd "("; e=parse_expr; 'Kwd ")" >] -> e
and parse_factor = parser
  | [< e1=parse_atom; stream >] ->
      (parser
         | [< 'Kwd "*"; e2=parse_factor >] -> e1 * e2
         | [< 'Kwd "/"; e2=parse_factor >] -> e1 / e2
         | [< >] -> e1) stream
and parse_expr = parser
  | [< e1=parse_factor; stream >] ->
      (parser
         | [< 'Kwd "+"; e2=parse_expr >] -> e1 + e2
         | [< 'Kwd "-"; e2=parse_expr >] -> e1 - e2
         | [< >] -> e1) stream

let () =
  Printf.printf "%d\n" (parse_expr(lex(Stream.of_string "1 + 2 * (3 + 4)")));;
person Community    schedule 18.04.2010

Я удивлен, что никто не сделал этого в Lex / Yacc или аналогичном.

Казалось бы, это дает кратчайший исходный код, который также можно было читать / поддерживать.

person Community    schedule 01.06.2009
comment
Я собирался сделать это с помощью ANTLR, но он был больше, чем вы могли подумать вначале xD - person fortran; 02.06.2009
comment
Lex / Yacc хорош для написания реальных парсеров, но если ваши требования достаточно просты, вы можете сделать это лучше (меньшего размера) и без него. - person Chris Lutz; 02.06.2009
comment
@Chris: Я склонен согласиться, но я подумал, что кто-то мог попробовать это. - person Mike Dunlavey; 02.06.2009
comment
Я мог бы попробовать, но в настоящее время у меня есть простое решение C до 209 символов. Попасть туда. - person Chris Lutz; 04.06.2009

PHP

Количество символов: 170

Полностью запутанная функция:

function a($a,$c='#\(([^()]*)\)#e',$d='a("$1","#^ *-?[\d.]+ *\S *-?[\d.]+ *#e","\$0")'){$e='preg_replace';while($a!=$b=$e($c,$d,$a))$a = $b;return$e('#^(.*)$#e',$d,$a);}

Более четкая функция:

function a($a, $c = '#\(([^()]*)\)#e', $d = 'a("$1", "#^ *-?[\d.]+ *\S *-?[\d.]+ *#e", "\$0")') {
    $e = 'preg_replace';
    while ($a != $b = $e($c, $d, $a)) {
        $a = $b;
    }
    return $e('#^(.*)$#e', $d, $a);
}

Тесты:

assert(a('1 + 3 / -8') === '-0.5');
assert(a('2*3*4*5+99') === '219');
assert(a('4 * (9 - 4) / (2 * 6 - 2) + 8') === '10');
assert(a('1 + ((123 * 3 - 69) / 100)') === '4');
assert(a('2.45/8.5*9.27+(5*0.0023)') === '2.68344117647');
assert(a(' 2 * 3 * 4 * 5 + 99 ') === '219');
person Community    schedule 23.06.2009
comment
Модификатор e для preg_replace нарушает правило, согласно которому evals запрещены. - person soulmerge; 07.07.2009

Perl

Количество символов: 93

Полностью запутанная функция: (93 символа, если объединить эти три строки в одну)

$_="(@ARGV)";s/\s//g;$n=qr/(-?\d+(\.\d+)?)/;
while(s.\($n\)|(?<=\()$n[-+*/]$n.eval$&.e){}
print

Очистить / полуобфусцированную функцию:

$_="(@ARGV)";            # Set the default var to "(" argument ")"
s/\s//g;                 # Strip all spaces from $_
$n=qr/(-?\d+(\.\d+)?)/;  # Compile a regex for "number"

# repeatedly replace the sequence "(" NUM ")" with NUM, or if there aren't
# any of those, replace "(" NUM OP NUM with the result
# of doing an eval on just the NUM OP NUM bit.
while(s{\($n\)|(?<=\()$n[-+*/]$n}{eval$&}e){}

# print $_
print

Я думаю, что это довольно хорошо объяснено в «ясной» версии. Два основных вывода заключаются в том, что вы можете сделать код единообразным, заключив аргумент в круглые скобки в начале (специальные символы стоимости), и что достаточно, хотя и в значительной степени неэффективно, обрабатывать данные только рядом с открытой круглой скобкой, заменяя это со своим результатом.

Этот код, вероятно, проще всего запустить как:

perl -le '$_="(@ARGV)";s/\s//g;$n=qr/(-?\d+(\.\d+)?)/;while(s.\($n\)|(?<=\()$n[-+*/]$n.eval$&.e){}print' '4 * (9 - 4) / (2 * 6 - 2) + 8'
person Community    schedule 31.05.2009
comment
Интересно, но разве eval нарушает правила? - person gnovice; 31.05.2009
comment
А, ладно, мне было непонятно, что eval запрещен; Я думал, что в описании говорилось, что его правило «Отсутствие БОДМЫ» было достаточной защитой от этого. Хорошо, я попробую другое решение без eval. - person Daniel Martin; 31.05.2009
comment
Да извини. Правила заключались в том, чтобы явно запретить eval. Это было всего лишь подозрение, что BODMAS должен предотвратить это, но не обязательно. - person Noldorin; 31.05.2009