Реализуйте Anyfix Notation!

16

В префиксной нотации оператор стоит перед аргументами, так что вы можете себе представить, что оператор вызывает next()рекурсивный вызов. В инфиксной нотации оператор идет между аргументами, поэтому вы можете представить его просто как дерево разбора. В постфиксной нотации оператор следует за аргументами, так что вы можете просто представить его как основанный на стеке.

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

Вам нужно будет поддерживать следующие команды:

(Arity 1)

  • дубликат
  • отрицательный

(Arity 2)

  • прибавление
  • умножение
  • равенство: возвращает 0или 1.

Вы можете использовать любые пять непробельных символов для этих команд. В демонстрационных целях я буду использовать "как дубликат, ×как умножение и +как дополнение.

Для литералов вам нужно только поддерживать неотрицательные целые числа, но ваш интерпретатор должен содержать все целые числа (в пределах (разумного) целочисленного диапазона вашего языка).

Давайте посмотрим на пример: 10+5. Хранилище должно вести себя как стек, а не как очередь. Итак, сначала стек начинается с [], а список операторов в очереди начинается с []. Затем 10вычисляется литерал, который составляет стек [10]. Далее +оценивается оператор , который требует двух аргументов. Однако в стеке есть только один аргумент, поэтому список операторов в очереди становится ['+']. Затем 5вычисляется литерал, который составляет стек [10, 5]. На этом этапе оператор '+'может быть оценен таким образом, что делает стек [15]и очередь[] .

Конечный результат должен быть [15]для + 10 5, 10 + 5и10 5 + .

Давайте посмотрим на пример сложнее: 10+". Стек и очередь начинаются как []и []. 10оценивается первым, что делает стек [10]. Далее +оценивается, что не меняет стек (потому что не хватает аргументов) и создает очередь ['+']. Затем "оценивается. Это может быть запущено сразу, так что это делает стек [10, 10]. +теперь можно оценить, чтобы стек стал [20]и очередью []. Конечный результат[20] .

Как насчет порядка операций?

Давайте посмотрим на ×+"10 10. Стек и очередь начинаются как []:

  • ×: Стек не изменяется и очередь становится ['×'].
  • +: Стек не изменяется и очередь становится ['×', '+'].
  • ": Стек не изменяется и очередь становится ['×', '+', '"'].
  • 10: Стек становится [10]. Несмотря на то, что ×должен быть первым оператором, который должен быть оценен, так как он появляется первым, он "может запускаться немедленно, и ни один из операторов не может раньше, поэтому он оценивается. Стек становится [10, 10]и очередью ['×', '+']. ×Теперь можно оценить, что делает стек [100]и очередь ['+'].
  • 10: Становится стек [100, 10], который позволяет +оцениваться. Стек становится [110]и очередью [].

Конечный результат есть [110].

Команды, используемые в этих демонстрациях, соответствуют командам языка anyfix; однако последний пример не будет работать из-за ошибки в моем интерпретаторе. (Отказ от ответственности: Ваши материалы не будут использоваться в интерпретаторе anyfix)

Вызов

Выберите набор из 5 непробельных нецифровых символов и создайте интерпретатор anyfix в соответствии с приведенными выше спецификациями. Ваша программа может выводить единственный массив или значение, содержащееся в нем; гарантируется, что стек значений будет содержать только одно значение в конце выполнения и что очередь операторов будет пустой в конце выполнения.

Это поэтому выигрывает самый короткий код в байтах.

Тестовые случаи

Для этих тестовых случаев дубликат есть ", отрицательный -, сложение +, умножение ×и равенство =.

Input -> Output
1+2×3 -> 9
1"+"+ -> 4
2"××" -> 16
3"×+" -> 18
3"+×" -> 36
123"= -> 1 ("= always gives 1)
1+2=3 -> 1
1"=2+ -> 3
1-2-+ -> -3
-1-2+ -> 3 (hehe, the `-1` becomes `+1` at the `-` rather than making the `2` a `-1`)
+×"10 10 -> 200 (after the 10 is duplicated (duplication is delayed), 10 + 10 is performed and then 20 * 10, giving 200)

правила

  • Применяются стандартные лазейки
  • Вы можете взять официального переводчика anyfix и поиграть в гольф, если хотите. Ожидайте, чтобы потерять ужасно.

Входные данные будут представлены в виде строки, а выходные данные - в виде массива, одного целого числа, из строкового представления любого из них. Вы можете предположить, что ввод будет содержать только пробелы, цифры и 5 выбранных вами символов.

* не совсем

HyperNeutrino
источник
2
Иди куда угодно * ™.
Джонатан Аллан
Каков результат оператора равенства? 0а 1?
Феликс Палмен
1
@JonathanAllan см. Выше; Я удалил командный рип
HyperNeutrino
1
@RickHitchcock Конечно.
HyperNeutrino
1
Возможно, вам следует включить ×+"10 10в тестовые наборы или любые другие примеры, которые 1) используют пробелы и 2) откладывают использование оператора дубликата (две вещи, которые я полностью пропустил).
Арно

Ответы:

5

JavaScript (ES6), 204 203 200 байт

Возвращает целое число.

e=>e.replace(/\d+|\S/g,v=>{for((1/v?s:v>','?u:b)[U='unshift'](v);!!u[0]/s[0]?s[U](u.pop()>'c'?s[0]:-S()):!!b[0]/s[1]?s[U](+eval(S(o=b.pop())+(o<'$'?'==':o)+S())):0;);},s=[],u=[],b=[],S=_=>s.shift())|s

Используемые символы:

  • +: дополнение
  • *: умножение
  • #: равенство
  • d: дубликат
  • -: отрицательный

Контрольные примеры

Отформатировано и прокомментировано

e => e.replace(                     // given an expression e, for each value v matching
  /\d+|\S/g, v => {                 // a group of digits or any other non-whitespace char.
    for(                            //   this loop processes as many operators as possible
      (                             //   insert v at the beginning of the relevant stack:
        1 / v ? s : v > ',' ? u : b //     either value, unary operator or binary operator
      )[U = 'unshift'](v);          //     (s[], u[] or b[] respectively)
      !!u[0] / s[0] ?               //   if there's at least 1 value and 1 unary operator:
        s[U](                       //     unshift into s[]:
          u.pop() > 'c' ?           //       for the duplicate operator:
            s[0]                    //         a copy of the last value
          :                         //       else, for the negative operator:
            -S()                    //         the opposite of the last value
        ) :                         //     end of unshift()
      !!b[0] / s[1] ?               //   if there's at least 2 values and 1 binary operator:
        s[U](                       //     unshift into s[]:
          +eval(                    //       the result of the following expression:
            S(o = b.pop()) +        //         the last value, followed by the
            (o < '$' ? '==' : o) +  //         binary operator o with '#' replaced by '=='
            S()                     //         followed by the penultimate value
          )                         //       end of eval()
        ) : 0;                      //     end of unshift()
    );                              //   end of for()
  },                                // end of replace() callback
  s = [],                           // initialize the value stack s[]
  u = [],                           // initialize the unary operator stack u[]
  b = [],                           // initialize the binary operator stack b[]
  S = _ => s.shift()                // S = helper function to shift from s[]
) | s                               // return the final result
Arnauld
источник
Не думай, что это работает -1+-2. Возвращает 3 вместо -3.
Рик Хичкок,
1
@RickHitchcock Насколько я понимаю, 2-й -должен быть применен -1немедленно.
Арно
Я думаю, что второй -будет идти с, 2так как он идет после другого оператора. Возможно, @HyperNeutrino может уточнить. Отрицательный оператор может быть неоднозначным в некоторых ситуациях.
Рик Хичкок,
3

JavaScript (ES6), 162 152 143 150 байт

(s,N=s.replace(/(-?\d+)-|-(-)/g,'- $1 ').match(/(- ?)*?\d+|R/g))=>+eval(`R=${N[0]>'9'?N[1]:N[0]},${s.match(/[+*=]/g).map((o,i)=>'R=R'+o+'='+N[i+1])}`)

Слегка разгульный

(s,
 N=s.replace(/(-?\d+)-|-(-)/g,'- $1 ').      //change postfix negatives to prefix,
                                             //and change "--" to "- - "
     match(/(- ?)*?\d+|R/g)                  //grab numbers and duplicates
)=>+eval(`R=${N[0] > '9' ?  N[1] : N[0]},    //handle a delayed duplicate
          ${s.match(/[+*=]/g).               //grab the operators
              map((o,i)=>'R=R'+o+'='+N[i+1]) //create a comma-separated list of assignments
           }
         `)

объяснение

Я использую *для умножения и Rдля дублирования. Другие операторы такие же, как в вопросе.

N становится массивом чисел (включая дубликаты).

replaceОбрабатывает случай , когда отрицательный знак приходит после того, как числа. Например, она будет меняться 1-в - 1и -1-к- -1 .

Внутри eval, s.matchсоздает массив бинарных операторов. Обратите внимание, что это всегда будет иметь на один элемент меньше, чем N.

Результатом функции является evalотображение чисел и операторов.

Вот что evalнаписано для каждого из тестовых случаев:

0+2*3        R=0,R=R+=2,R=R*=3        = 6 
1+2*3        R=1,R=R+=2,R=R*=3        = 9 
1R+R+        R=1,R=R+=R,R=R+=R        = 4 
2R**R        R=2,R=R*=R,R=R*=R        = 16 
3R*+R        R=3,R=R*=R,R=R+=R        = 18 
3R+*R        R=3,R=R+=R,R=R*=R        = 36 
123R=        R=123,R=R==R             = 1 
1+2=3        R=1,R=R+=2,R=R==3        = 1 
1R=2+        R=1,R=R==R,R=R+=2        = 3 
1-2-+        R=- 1,R=R+=- 2           = -3 
-1-2+        R=1,R=R+=2               = 3 
*+R10 10     R=10,R=R*=10,R=R+=10     = 110 
+*R10 10     R=10,R=R+=10,R=R*=10     = 200 
-1+--2       R=-1,R=R+=- -2           = 1 
-1+-2        R=-1,R=R+=-2             = -3 

Оператор запятой в выражении JavaScript возвращает результат его последнего выражения, поэтому map автоматически возвращает пригодное для использования выражение.

+Знак необходим , прежде чем evalизменить , trueчтобы 1и falseв0 .

Использование Rв качестве переменной и оператора дубликата значительно упрощает mapподвыражения.

Тестовые случаи:

Рик Хичкок
источник
2
Я не думаю, что replaceработает как задумано. Это возвращает 3для, -1+--2и я думаю, что 1это будет правильно ( 1изменения подписывают три раза, прежде чем есть второй аргумент для +доступных, в результате -1 + 2).
Феликс Пальмен,
Отличный улов, @FelixPalmen. Сейчас исправлено.
Рик Хичкок,
2

JavaScript, 321 311 байт

_="f=a=>(a.replace(/\\d+|./g,mq!~(b='+*=\"- '.indexOf(a))|b>2j3j4j5&&o+aw1/y?(y*=-1wcz:1/y?oywcz:hz,rql.map(z=>m(lki,1)[0],i)),hq1/s[1]?sk0,2,+eval(`y${a=='='?a+a:a}s[1]`)):cz,cqlki,0,a),s=[],l=[],e='splice'),s)z(a,i)ys[0]w)^r``:q=z=>os.unshift(k[e](j?b-";for(i of"jkoqwyz")with(_.split(i))_=join(pop());eval(_)

Попробуйте онлайн!

Пять символов такие же, как в вопросе, за исключением *умножения.
Скрипт сжимается с помощью RegPack . Исходный сценарий сохраняется в переменной _после оценки.


источник
Не думай, что это работает -1+-2. Возвращает 3 вместо -3.
Рик Хичкок,
@RickHitchcock. Почему вы считаете, что он должен вернуться -3вместо 3?
Возможно, я неправильно понимаю отрицательный оператор. Как правило, -1 + -2есть -3, но должен ли он быть проанализирован как --1 + 2вместо?
Рик Хичкок,
1
@RickHitchcock. Я уверен, что результат есть 3. Прежде чем 2даже прийти в стек, второй -оценивается, и, следовательно, у нас есть, 1 2 +что действительно 3. Ах, и, вероятно, вы тоже должны отредактировать свой ответ.
Возможно Вы правы. Вы и Арно получите один и тот же ответ, и я попросил у ОП уточнения. Я бы проголосовал снова, если бы мог.
Рик Хичкок,
1

Haskell , 251 байт

(#([],[]))
(x:r)#(o,n)|x>'9'=r#h(o++[x],n)|[(a,t)]<-lex$x:r=t#h(o,read a:n)
_#(o,n:_)=n
h=e.e
e(o:s,n:m:r)|o>'N'=e(s,g[o]n m:r)
e(o:s,n:r)|o=='D'=e(s,n:n:r)|o=='N'=e(s,-n:r)
e(o:s,n)|(p,m)<-e(s,n)=(o:p,m)
e t=t
g"a"=(+)
g"m"=(*)
g"q"=(((0^).abs).).(-)

Попробуйте онлайн! Используются следующие символы: aдля сложения, mдля умножения, qдля равенства, Dдля дублирования и Nдля отрицания. (Я хотел использовать eдля равенства, но столкнулся с проблемой, которая lexразбирает 2e3как число.) Пример использования: (#([],[])) "2a3 4m"урожайность 20.

Laikoni
источник
1

6502 машинный код (C64), 697 байт

00 C0 A2 00 86 29 86 26 86 27 86 28 86 4B 86 4C 86 CC 20 E4 FF F0 FB C9 0D F0
10 C9 20 30 F3 A6 27 9D B7 C2 20 D2 FF E6 27 D0 E7 C6 CC A9 20 20 1C EA A9 0D
20 D2 FF 20 95 C0 20 09 C1 20 95 C0 A6 26 E4 27 F0 4E BD B7 C2 E6 26 C9 20 F0
E8 C9 2D D0 09 A6 4C A9 01 9D B7 C3 D0 32 C9 22 D0 09 A6 4C A9 02 9D B7 C3 D0
25 C9 2B D0 09 A6 4C A9 81 9D B7 C3 D0 18 C9 2A D0 09 A6 4C A9 82 9D B7 C3 D0
0B C9 3D D0 0D A6 4C A9 83 9D B7 C3 E6 28 E6 4C D0 A3 4C 8A C2 A6 29 F0 6F A4
28 F0 6B CA F0 4B C6 28 A6 4B E6 4B BD B7 C3 F0 F5 30 14 AA CA D0 0B 20 7B C2
20 E1 C1 20 4D C2 D0 D9 20 5C C2 D0 D4 29 0F AA CA D0 0B 20 6D C2 20 08 C2 20
4D C2 D0 C3 CA D0 0B 20 6D C2 20 16 C2 20 4D C2 D0 B5 20 6D C2 20 F4 C1 20 4D
C2 D0 AA A4 4B B9 B7 C3 F0 02 10 AC C8 C4 4C F0 0F B9 B7 C3 F0 F6 30 F4 AA A9
00 99 B7 C3 F0 A6 60 A0 00 A6 26 E4 27 F0 15 BD B7 C2 C9 30 30 0E C9 3A 10 0A
E6 26 99 37 C4 C8 C0 05 D0 E5 C0 00 F0 08 A9 00 99 37 C4 20 39 C2 60 A2 FF E8
BD 37 C4 D0 FA A0 06 88 CA 30 0A BD 37 C4 29 0F 99 37 C4 10 F2 A9 00 99 37 C4
88 10 F8 A9 00 8D 3D C4 8D 3E C4 A2 10 A0 7B 18 B9 BD C3 90 02 09 10 4A 99 BD
C3 C8 10 F2 6E 3E C4 6E 3D C4 CA D0 01 60 A0 04 B9 38 C4 C9 08 30 05 E9 03 99
38 C4 88 10 F1 30 D2 A2 06 A9 00 9D 36 C4 CA D0 FA A2 10 A0 04 B9 38 C4 C9 05
30 05 69 02 99 38 C4 88 10 F1 A0 04 0E 3D C4 2E 3E C4 B9 38 C4 2A C9 10 29 0F
99 38 C4 88 10 F2 CA D0 D6 C0 05 F0 06 C8 B9 37 C4 F0 F6 09 30 9D 37 C4 E8 C8
C0 06 F0 05 B9 37 C4 90 F0 A9 00 9D 37 C4 60 A9 FF 45 FC 85 9F A9 FF 45 FB 85
9E E6 9E D0 02 E6 9F 60 A2 00 86 9F A5 FB C5 FD D0 07 A5 FC C5 FE D0 01 E8 86
9E 60 A5 FB 18 65 FD 85 9E A5 FC 65 FE 85 9F 60 A9 00 85 9E 85 9F A2 10 46 FC
66 FB 90 0D A5 FD 18 65 9E 85 9E A5 FE 65 9F 85 9F 06 FD 26 FE CA 10 E6 60 20
33 C1 A6 29 AD 3D C4 9D 3F C4 AD 3E C4 9D 3F C5 E6 29 60 A6 29 A5 9E 9D 3F C4
A5 9F 9D 3F C5 E6 29 60 A6 29 BD 3E C4 9D 3F C4 BD 3E C5 9D 3F C5 E6 29 60 C6
29 A6 29 BD 3F C4 85 FD BD 3F C5 85 FE C6 29 A6 29 BD 3F C4 85 FB BD 3F C5 85
FC 60 A6 29 BD 3E C5 10 13 20 7B C2 20 E1 C1 20 4D C2 A9 2D 20 D2 FF A6 29 BD
3E C5 8D 3E C4 BD 3E C4 8D 3D C4 20 8B C1 A9 37 A0 C4 4C 1E AB

Онлайн демо

Использование sys49152 , затем введите выражение anyfix и нажмите клавишу возврата.

  • близко к отсутствию проверки ошибок, так что ожидайте забавных выводов для недопустимых выражений.
  • символ для умножения *, все остальные, как предложено.
  • максимальная длина ввода - 256 символов, в очереди может быть не более 127 операторов.
  • Процедура ввода не обрабатывает управляющие символы, поэтому не допускайте опечаток;)
  • целые числа имеют 16-битную подпись, переполнение будет незаметно обтекать.
  • Количество байт является немного большим , потому что этот процессор даже не знает , умножения и C64 OS / ROM не дает какую - либо целочисленная арифметику или преобразования в / из десятичных строк.

Вот читаемый исходный код ассемблера в стиле ca65 .

Феликс Палмен
источник
1

VB.NET (.NET 4.5) 615 576 байт

-39 байтов благодаря Феликсу Пальмену, изменив \r\nна\n

Требуется Imports System.Collections.Generic(включено в число байтов)

Dim s=New Stack(Of Long),q=New List(Of String),i=Nothing,t=0,c=0
Function A(n)
For Each c In n
If Long.TryParse(c,t)Then
i=i &"0"+t
Else
If c<>" "Then q.Add(c)
If i<>A Then s.Push(i)
i=A
End If
If i=A Then E
Next
If i<>A Then s.Push(i)
E
A=s
End Function
Sub E()
c=s.Count
If c=0Then Exit Sub
For Each op In q
If op="-"Or op="d"Or c>1Then
Select Case op
Case"*"
s.Push(s.Pop*s.Pop)
Case"+"
s.Push(s.Pop+s.Pop)
Case"="
s.Push(-(s.Pop=s.Pop))
Case"-"
s.Push(-s.Pop)
Case"d"
s.Push(s.Peek)
End Select
q.Remove(op)
E
Exit Sub
End If
Next
End Sub

Точкой входа является функция A, которая принимает строку в качестве входных данных и возвращает a Stack(Of Long).

Условные обозначения:

  • Дополнение - +
  • Умножение - *
  • Равенство - =
  • Отрицание - -
  • Дублирование - d

Обзор:

Функция Aпринимает входные данные и токенизирует их. Он использует Long?промежуточный итог для многозначных целых чисел, что Nothingозначает , что мы в настоящее время не читаем целое число.

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

Я объявляю глобальные параметры сразу, чтобы сохранить байты как при объявлении, так и при передаче параметров.

Возвращаемое значение из Aзадается путем присвоения значения соответствующей переменной A.

VB Trueэквивалентен -1, так что операция должна отменить результат, чтобы получить правильное значение.

Попробуйте онлайн!

Брайан Дж
источник
Предлагаем добавить Попробуйте онлайн!
Феликс Пальмен,
Кстати, с Imports, я получаю количество байтов только 576- не могли бы вы подсчитать?
Феликс Пальмен,
@FelixPalmen Я посчитал \r\nвместо \n, так что вот где расхождение.
Брайан Джей
@FelixPalmen Добавил TIO, спасибо, что напомнили мне! :) (О, я не осознавал, что ты уже сделал это для этого поста)
Брайан Дж