Підбиваючи підсумки? Оце мій форте!


18

Вступ

Форте - дуже своєрідна езотерична мова, заснована на концепції модифікації значень чисел. У числах Форте не константи, а змінні, ви можете використовувати LETінструкцію, щоб призначити їм нові значення.

Наприклад, після виконання LET 2=4-1відтепер 2набуває значення 3, що означає, що щоразу, коли значення 2з'являється у виразі, воно замість цього "замінюється" на 3. Вираз (1+1)*2тепер оцінюватиме 9.

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

Змагання

Вам потрібно написати інтерпретатора для спрощеного підмножини LETвиразів Forte .

Ви отримаєте як введення ряд рядків, наступних за цією граматикою:

<line>::= <number>=<expression>

<expression>::= <number>|<expression>+<number>

Примітка: ця граматика не є дійсною Forte, оскільки в ній відсутні номери рядків, LET та круглі дужки (які завжди обов'язкові)

Тобто вам потрібно буде мати справу лише з обчисленням підсумків та призначенням значень чисел. Дужки не будуть присутні у введенні, і кожен вираз повинен бути оцінений зліва направо: будьте уважні, що часткові результати впливають на повторні визначення!

Числа завжди будуть невід'ємними цілими числами, аж до межі нативного цілого типу вашої мови (або 2 ^ 32, залежно від того, що вище).

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

Це , виграє найкоротший код (у байтах)!

Інші правила

  • Формат введення є гнучким, ви можете, наприклад, взяти один рядок з новими рядками, списком рядків, списком списків чисел ... Те саме стосується виводу, доки зрозуміло, що результат кожного виразу в вхід.
  • Ви можете подати або функцію, повну програму, або рішення, яке потрібно запустити у середовищі REPL, викликаючи його один раз для кожного рядка.
  • Стандартні лазівки заборонені, зокрема ви не можете викликати зовнішній інтерпретатор Forte у своєму коді.

Приклади

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

5=4
4
6=5
4        # 5 -> 4
7=1+2+5
7
7=5+2+1
4        # Order of operations matters! 5+2 -> 4+2 -> 6 -> 4
18=5+6+7
12
5=3
3        # Remember: 5 -> 4
10=6+4
3        # 6 -> 4 -> 3, 3+3 = 6 -> 3

Чи 0дійсне число?
orlp

@orlp 0дійсний ("Числа завжди будуть невід'ємними цілими числами")
Лев,

Чи слід приймати будь-які арифметичні оператори?
bacchusbeale

@bacchusbeale Ні, просто підсумок.
Лев

Чи існує якась максимальна кількість чи вона така велика, як цілий цілий тип мови? Крім того, було б недійсним вихід, щоб повернути список номерів, одне з яких - результат, правда? (наприклад, друк словника / карти, на яке число йде на яке число)
zgrep

Відповіді:


4

Желе , 28 байт

®y$ÐL
ṪÇ+Ç¥/Ṅ;®⁸Ǥ;©
⁸©ḷƓÇ€¤

Спробуйте в Інтернеті!

Це одна з небагатьох програм Jelly, де здається, що терзивіше взяти дані зі стандартного введення. Це повна програма (написання функції було б коротше, але заборонено правилами PPCG, оскільки вона не працюватиме правильно вдруге). Формат введення виглядає приблизно так:

[[5,[4]],[6,[5]],[7,[1,2,5]],[7,[5,2,1]],[18,[5,6,7]],[5,[3]],[10,[6,4]]]

Пояснення

Допоміжна функція 1Ŀ (перекладіть ціле число до його значення)

®y$ÐL
   ÐL   Repeatedly, until there are no further changes,
  $       apply the following unary function to {the input}:
 y          replace values using the mapping table
®             stored in the register.
        {Then return the eventual result.}

Досить зручно, ця допоміжна функція буде працювати коректно або на одному значенні, або на списку значень, залежно від yвизначеного способу . Якщо для одного значення задано більше одного відображення, ми беремо перше відображення з таблиці. Таблиця відображення зберігається в реєстрі (що в основному є лише змінною; Jelly має лише одну змінну).

Допоміжна функція 2Ŀ (оцініть одну інструкцію LET)

ṪÇ+Ç¥/Ṅ;®⁸Ǥ;©
Ṫ               On the last element of {the input},
 Ç              Run 1Ŀ,
     /          left fold it via
    ¥             the following binary function:
  +                 add {the two arguments}
   Ç                and run 1Ŀ on {the result},
      Ṅ         write {the result} (and a newline) to standard output,
       ;®       append the value of the register,
            ;   prepend
           ¤      the following value:
         ⁸          {the input, without its last element}
          Ç         with 1Ŀ run on it
             ©  and store that value in the register {and return it}.

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

Основна програма

⁸©ḷƓÇ€¤
 ©       Initialize the mapping table
⁸        with the empty string (which is also the empty list)
  ḷ      then evaluate and discard
      ¤  the following value:
   Ɠ       a line from standard input, parsed into a data structure
    Ç€     with each element transformed via 2Ŀ
         {and leave the empty string to be printed implicitly}

Зазвичай, він дає нам перший аргумент командного рядка при запуску в цьому контексті, але його немає (ми беремо введення зі стандартного вводу), тому він працює в альтернативному режимі, який дає нам нульову рядок. Необхідність ініціалізації реєстру (від його значення за замовчуванням 0, яке виходить з ладу y) означає, що ми не можемо неявно згадувати введення користувача, це означає, що брати його зі стандартного вводу ( Ɠ) так само дешево, як було б взяти його з аргумент командного рядка ( ³або ) і можливість доступу до альтернативного використання означає, що незвична (для Jelly) форма введення насправді на байт коротша.

Можливо, це неможливо. Я досі не зрозумів, чому другий рядок повинен говорити, ⁸Ǥ;а не просто ;@Ç- два мають бути рівнозначними, наскільки я розумію Jelly, враховуючи відсутність використання µ// ð/, øале останній чомусь виходить з ладу. Так само існують різні інші способи переставити програму, не втрачаючи байтів, тому, можливо, я пропустив спосіб зробити речі трохи коротшими.

До речі, зміна в останньому рядку ;дає цікавий погляд на внутрішню роботу програми, оскільки потім буде виведена "історія реєстру", яка неявно виводиться за значеннями, що повертаються 2Ḷ.


5

Perl 5 , 92 байти

90 байт коду + -plпрапори.

sub f{($b=$h{$a=pop}//$a)!=$a?f($b):$a}s%(\d+)\+(\d+)%f($1)+f$2%e&&redo;/=/;$_=$h{f$`}=f$'

Спробуйте в Інтернеті!

Я використовую хештел, %hщоб зберігати відображення між номерами.
Функція ( sub) fповертає число, на яке його карта введення (або його введення, якщо воно не відображено): $h{$a=pop}отримує число, до якого вводиться вхід. Якщо це не, завдяки //$aзначенню ($b=$h{$a=pop}//$a)є input ( $a). Ми переконуємося, що ці значення не є самим входом, щоб не циклічно фіксувати ( !=$a). Тоді ми або рекурсивно дзвонимо, fабо повертаємо дані.
Основна програма складається з двох етапів:
- s%(\d+)\+(\d+)%f($1)+f$2%e&&redoоцінює перше доповнення з правого боку, в той час як ще є доповнення: воно замінює x+yза результатами оцінки f(x)+f(y).
- /=/;$_=$h{f$`}=f$'виконує завдання:/=/дозволяє отримати доступ до лівої $`та правої частини $', а потім $h{f$`}=f$'виконувати завдання. І ми також присвоюємо це тому, $_що неявно друкується після кожного рядка.


5

JavaScript (Node.js) , 81 байт

v=x=>(v[x]=v[x]||x,v[x]-x?v(v[x]):x)
f=x=>l=>v[v(x)]=l.reduce((p,x)=>v(v(x)+p),0)

Спробуйте в Інтернеті!

Отримує вхід, викликаючи f зі значенням, якому слід призначити, а потім викликає результат цього масиву значень, який потрібно скласти разом. (тобто f(5)([4])) Повторіть для декількох рядків.

vвикористовується як функція для обчислення фактичного поточного значення числа, а також як об'єкт для зберігання фактичних значень. Спочатку v[x]=v[x]||xзабезпечує те, що v[x]визначено. v[x]-xвиконує порівняння, щоб визначити, чи є це фактичне число чи ні. Якщо число не відображається в собі, v(v[x])рекурсивно намагається знову, інакше повернутися x.

f виконує обчислення та призначення, зберігши збереження одного байта, де другий виклик повертає обчислене значення.


3

Haskell , 116 113 108 106 байт

(#)=until=<<((==)=<<)
e?((n,s):r)|m<-foldl1(\a b->e#(e#a+e#b))s=m:(\x->last$m:[e#x|x/=e#n])?r
e?r=[]
(id?)

Спробуйте в Інтернеті! Кожне рівняння 4=3+1+5позначається як кортеж (4,[3,1,5]). Анонімна функція (id?)приймає список таких кортежів і повертає список усіх проміжних результатів.

#- це функція пошуку точної точки даної функції eта початкового значення x.

Функція ?приймає оціночну функцію eі рекурсивно розв'язує кожне рівняння. foldl1(\a b->e#(e#a+e#b))sоцінює праву частину рівняння і зберігає результат m, наприклад, для 4=3+1+5обчислень eval(eval(eval 3 + eval 1) + eval 5), де кожне evalє фіксованою точкою e. Тоді функція eval змінюється, щоб враховувати нове призначення n: (\x->last$m:[e#x|x/=e#n])те саме, що \x -> if x == eval n then m else eval x.

Початкова функція оцінювання - це те, idщо відображає кожне ціле число до себе.


Завдяки Ørjan Johansen за більш коротку функцію фіксації, заощаджуючи 2 байти!


Хороша робота! До речі, вам потрібно повернути всі проміжні результати, щоб ви могли відмовитисяlast.
Лев

2
(#)e=until((==)=<<e)eабо (#)=until=<<((==)=<<)коротше.
Ørjan Johansen

@ ØrjanJohansen Дякую багато!
Лайконі

3

oK, 48 байт

a:[];s:{*(a@x;x)^0N}/;f:{a[s@x]:y:{s@x+y}/s'y;y}

Використання: f[5;1 2 3] / 5=1+2+3

Спробуйте в Інтернеті!


Якщо ви не заперечуєте за тим, щоб мати верхню межу чисел, якими ви можете скористатися, наприклад, лише для використання цифр 0наскрізь 998, тоді достатньо наступного ( 41 байт ± кілька, залежно від максимуму):

a:!999;s:(a@)/;f:{a[s@x]:y:{s@x+y}/s'y;y}

Пояснення:

; відокремлює три визначення.

aце словник / карта чисел. У першому випадку, це фактичний, порожній словник [], у другому випадку це список номерів 0в 998.

s- це функція, яка знаходить "отримане" число, коли йому дано число. На /кінці функції означає, що вона застосовуватиметься до власного виводу, поки вихід не перестане змінюватися.

Останній біт f, означає, що:

f:{                      } /function called f, input number x, list y
                    s'y    /apply s to every number in the list
                   /       /fold through the list
            {s@x+y}        /    sum the two numbers, apply s
   a[s@x]:                 /set the s(x) to map to the final sum
          y:           ;y  /redefine y to be the final sum, then return it

3

Python 3, 146 132 130 байт

14 байт збережено завдяки @Dada
2 байти збережено завдяки @ mbomb007

d={}
g=lambda x:d.get(x)and x!=d[x]and g(d[x])or x
def f(t):
 for n,(s,*r)in t:
  for b in r:s=g(g(s)+g(b))
  d[g(n)]=s;yield g(n)

Спробуйте в Інтернеті!

Отримує вхід у вигляді кортежів рівнянь [ x = y + z + wяк (x, (y, z, w))], виводить через генератор.


Чи можете ви показати приклад виклику, щоб його можна було перевірити?
Лев

1
@Leo додав TIO.
Уріель

1
gможливо, можна було б написати g=lambda x:d.get(x)and d[x]!=x and g(d[x])or x. І я думаю, ви можете використовувати один пробіл для відступу замість 2. Це повинно отримати вас до [132 байтів] ( Спробуйте в Інтернеті! ).
Дада

1
@Dada спасибі! занадто погано, що у них немає відступу на півпростору: P
Уріель,

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