Молекули до атомів


44

Змагання

Напишіть програму, яка може розбити вхідну хімічну формулу (див. Нижче) та вивести її відповідні атоми у формі element: atom-count.


Вхідні дані

Зразок введення:

H2O

Ваш вхід завжди буде містити принаймні один елемент, але не більше десяти. Ваша програма повинна приймати вхідні дані, які містять дужки, які можуть бути вкладені.

Елементи в рядках завжди будуть відповідати [A-Z][a-z]*, тобто вони завжди почнуться з великої літери. Числа завжди будуть одноцифровими.


Вихідні дані

Вибірка вибірки (для вищевказаного входу):

H: 2
O: 1

Необов'язково після вашого результату може слідувати новий рядок.


Порушення молекули

Числа праворуч від набору дужок розподіляються кожному елементу всередині:

Mg(OH)2

Потрібно вивести:

Mg: 1
O: 2
H: 2

Цей же принцип застосовується до окремих атомів:

O2

Потрібно вивести:

O: 2

А також ланцюжок:

Ba(NO2)2

Потрібно вивести:

Ba: 1
N: 2
O: 4

Приклади

> Ba(PO3)2
Ba: 1
P: 2
O: 6

> C13H18O2
C: 13
H: 18
O: 2

> K4(ON(SO3)2)2
K: 4
O: 14
N: 2
S: 4

> (CH3)3COOC(CH3)3
C: 8
H: 18
O: 2

> (C2H5)2NH
C: 4
H: 11
N: 1

> Co3(Fe(CN)6)2
Co: 3
Fe: 2
C: 12
N: 12

Вводи позначаються стрілкою (більшим, ніж знаком; >).

Табло

Щоб ваш рахунок з’явився на дошці, він повинен бути у такому форматі:

# Language, Score

Або якщо ви заробили бонус:

# Language, Score (Bytes - Bonus%)

Редагувати: квадратні дужки вже не є частиною питання. Будь-які відповіді, опубліковані до 3:00 UTC, 23 вересня, є безпечними, і ця зміна не впливатиме.


Які дозволені форми введення?
Оберон

1
@ZachGates Краще, що нам дозволено також підтримувати, але майте на увазі, що квадратні дужки все ще неправильні. Квадратні дужки AFAIK у хімічних формулах застосовуються лише до вказаної концентрації. Наприклад: [HCl] = 0.01 mol L^-1.
orlp

Вони є, але для всіх інтенсивних цілей ми також збираємось використовувати їх для групування. @orlp Якщо тільки це дійсно не велика справа; в такому випадку я повністю зняв дужки.
Зак Гейтс

Дивіться розділ "Приклади". Ви щось запитуєте? Введення @Oberon позначаються а >.
Зак Гейтс

1
Лише зауважте, що в прикладах все ще є елементи з багатоцифровим числом атомів.
ПрограмістDan

Відповіді:


11

CJam, 59 57 байт

q{:Ci32/")("C#-"[ ] aC~* Ca C+"S/=~}%`La`-S%$e`{~": "@N}/

Спробуйте його в Інтернеті в інтерпретаторі CJam .

Як це працює

q             e# Read all input from STDIN.
{             e# For each character:
  :Ci         e#   Save it in C and cast to integer.
  32/         e#   Divide the code point by 32. This pushes
              e#   2 for uppercase, 3 for lowercase and 1 for non-letters.
  ")("C#      e#   Find the index of C in that string. (-1 if not found.)
  -           e#   Subtract. This pushes 0 for (, 1 for ), 2 for digits,
              e#   3 for uppercase letters and 4 for lowercase letters.

 "[ ] aC~* Ca C+"

 S/           e#   Split it at spaces into ["[" "]" "aC~*" "Ca" "C+"].
 =~           e#   Select and evaluate the corresponding chunk.
              e#     (   : [    : Begin an array.
              e#     )   : ]    : End an array.
              e#     0-9 : aC~* : Wrap the top of the stack into an array
              e#                  and repeat that array eval(C) times.
              e#     A-Z : Ca   : Push "C".
              e#     a-z : C+   : Append C to the string on top of the stack.
}%            e#
`             e# Push a string representation of the resulting array.
              e# For input (Au(CH)2)2, this pushes the string
              e# [[["Au" [["C" "H"] ["C" "H"]]] ["Au" [["C" "H"].["C" "H"]]]]]
La`           e# Push the string [""].
-             e# Remove square brackets and double quotes from the first string.
S%            e# Split the result at runs of spaces.
$e`           e# Sort and perform run-length encoding.
{             e# For each pair [run-length string]:
  ~           e#   Dump both on the stack.
  ": "        e#   Push that string.
  @N          e#   Rotate the run-length on top and push a linefeed.
}/            e#

10

Pyth, 66 65 байт

VrSc-`v::z"([A-Z][a-z]*)""('\\1',),"",?(\d+)""*\\1,"`(k))8j": "_N

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


3
+1. Три відповіді за годину? Приємно.
Zach Gates

10

Python3, 157 154 байт

import re
s=re.sub
f=s("[()',]",'',str(eval(s(',?(\d+)',r'*\1,',s('([A-Z][a-z]*)',r'("\1",),',input()))))).split()
for c in set(f):print(c+":",f.count(c))

Підтримує лише введення даних, використовуючи звичайні дужки.

Перш ніж створити рішення для гольфу за допомогою evalвище, я створив це довідкове рішення, яке мені здалося дуже елегантним:

import re, collections

parts = filter(bool, re.split('([A-Z][a-z]*|\(|\))', input()))
stack = [[]]
for part in parts:
    if part == '(':
        stack.append([])
    elif part == ')':
        stack[-2].append(stack.pop())
    elif part.isdigit():
        stack[-1].append(int(part) * stack[-1].pop())
    else:
        stack[-1].append([part])

count = collections.Counter()
while stack:
    if isinstance(stack[-1], list):
        stack.extend(stack.pop())
    else:
        count[stack.pop()] += 1

for e, i in count.items():
    print("{}: {}".format(e, i))

6

JavaScript ES6, 366 байт

function f(i){function g(a,b,c){b=b.replace(/[[(]([^[(\])]+?)[\])](\d*)/g,g).replace(/([A-Z][a-z]?)(\d*)/g,function(x,y,z){return y+((z||1)*(c||1))});return(b.search(/[[(]/)<0)?b:g(0,b)}return JSON.stringify(g(0,i).split(/(\d+)/).reduce(function(q,r,s,t){(s%2)&&(q[t[s-1]]=+r+(q[t[s-1]]||0));return q},{})).replace(/["{}]/g,'').replace(/:/g,': ').replace(/,/g,'\n')}

JS Fiddle: https://jsfiddle.net/32tunzkr/1/

Я впевнений, що це можна скоротити, але мені потрібно повернутися до роботи. ;-)


2
Я впевнений, що його можна також скоротити. Оскільки ви вимагаєте використовувати ES6, ви можете почати, використовуючи позначення великої стрілки для створення функцій. І неявне returnтвердження. Цього зараз має вистачити.
Ісмаїл Мігель

Ви також replaceбагато використовуєте, щоб ви могли зберегти кілька байт, використовуючи xyz[R='replace'](...)перший і abc[R] (...)наступний раз.
DankMemes

6

SageMath , 156 148 байт

import re
i=input()
g=re.sub
var(re.findall("[A-Z][a-z]?",i))
print g("(\d+).(\S+)\D*",r"\2: \1\n",`eval(g("(\d+)",r"*\1",g("([A-Z(])",r"+\1",i)))`)

Спробуйте в Інтернеті тут (сподіваємось, посилання спрацює, можливо, потрібен онлайн-акаунт)

Примітка. Якщо ви намагаєтесь в Інтернеті, вам потрібно буде замінити input()рядок (наприклад "(CH3)3COOC(CH3)3")

Пояснення

Sage дозволяє спростити алгебраїчні вирази, якщо вони знаходяться у потрібному форматі (див. "Символічне маніпулювання" цього посилання). Регекси всередині eval () в основному служать для отримання вхідного рядка у потрібний формат, наприклад, наприклад:

+(+C+H*3)*3+C+O+O+C+(+C+H*3)*3

eval()потім спростить це до:, 8*C + 18*H + 2*Oі тоді це лише питання форматування виводу за допомогою іншої підстановки з регулярними виразами.


5

Python 3, 414 байт

Я сподіваюся, що порядок результату не враховується.

import re
t=input().replace("[", '(').replace("]", ')')
d={}
p,q="(\([^\(\)]*\))(\d*)","([A-Z][a-z]*)(\d*)"
for i in re.findall(q,t):t = t.replace(i[0]+i[1],i[0]*(1if i[1]==''else int(i[1])))
r=re.findall(p,t)
while len(r)>0:t=t.replace(r[0][0]+r[0][1],r[0][0][1:-1]*(1if r[0][1]==''else int(r[0][1])));r=re.findall(p,t)
for i in re.findall(q[:-5], t):d[i]=d[i]+1if i in d else 1
for i in d:print(i+': '+str(d[i]))

5

Javascript (ES6), 286 284

Не набагато коротший, ніж інший ES6, але я дав це з усіх сил. Примітка: ця помилка вийде, якщо ви дасте їй порожній рядок або більшість недійсних даних. Також очікує, що всі групи налічують більше 1 (тобто ні CO[OH]). Якщо це порушує будь-які правила виклику, дайте мені знати.

a=>(b=[],c={},a.replace(/([A-Z][a-z]*)(?![0-9a-z])/g, "$11").match(/[A-Z][a-z]*|[0-9]+|[\[\(]/g).reverse().map(d=>(d*1==d&&b.push(d*1),d.match(/\(|\[/)&&b.pop(),d.match(/[A-Z]/)&&eval('e=b.reduce((f,g)=>f*g,1),c[d]=c[d]?c[d]+e:e,b.pop()'))),eval('g="";for(x in c)g+=x+`: ${c[x]}\n`'))

Використовується підхід на основі стека. По-перше, він попередньо обробляє рядок для додавання 1до будь-якого елемента без числа, тобто Co3(Fe(CN)6)2стає Co3(Fe1(C1N1)6)2. Потім він проходить цикл у зворотному порядку і накопичує кількість елементів.

a=>(
  // b: stack, c: accumulator
  b=[], c={},

  // adds the 1 to every element that doesn't have a count
  a.replace(/([A-Z][a-z]*)(?![0-9a-z])/g, "$11")

    // gathers a list of all the elements, counts, and grouping chars
    .match(/[A-Z][a-z]*|[0-9]+|[\[\(]/g)

    // loops in reverse order
    .reverse().map(d=>(

       // d*1 is shorthand here for parseInt(d)
       // d*1==d: true only if d is a number
       // if it's a number, add it to the stack
       d * 1 == d && b.push(d * 1),

       // if there's an opening grouping character, pop the last item
       // the item being popped is that group's count which isn't needed anymore
       d.match(/\(|\[/) && b.pop(),

       // if it's an element, update the accumulator
       d.match(/[A-Z]/) && eval('

         // multiplies out the current stack
         e = b.reduce((f, g)=> f * g, 1),

         // if the element exists, add to it, otherwise create an index for it
         c[d] = c[d] ? c[d] + e : e,

         // pops this element's count to get ready for the next element
         b.pop()
       ')
  )),

  // turns the accumulator into an output string and returns the string
  eval('
    g="";

    // loops through each item of the accumulator and adds it to the string
    // for loops in eval always return the last statement in the for loop
    // which in this case evaluates to g
    for(x in c)
      g+=x+`: ${c[x]}\n`
  ')
)

Скрипка


5

Perl, 177 172 байт

171 байт код + 1 байт параметру командного рядка

Гаразд, так що я, можливо, трохи захопився регексом на цьому ...

s/(?>[A-Z][a-z]?)(?!\d)/$&1/g;while(s/\(([A-Z][a-z]?)(\d+)(?=\w*\W(\d+))/$2.($3*$4).$1/e||s/([A-Z][a-z]?)(\d*)(\w*)\1(\d*)/$1.($2+$4).$3/e||s/\(\)\d+//g){};s/\d+/: $&\n/g

Приклад використання:

echo "(CH3)3COOC(CH3)3" | perl -p entry.pl

2

Математика, 152 байти

f=TableForm@Cases[PowerExpand@Log@ToExpression@StringReplace[#,{a:(_?UpperCaseQ~~___?LowerCaseQ):>"\""<>a<>"\"",b__?DigitQ:>"^"<>b}],a_. Log[b_]:>{b,a}]&

Сказане визначає функцію, fяка приймає рядок як вхідний. Функція бере рядок і загортає кожне ім'я елемента в лапки і додає оператор експоненції інфіксу перед кожним числом, а потім інтерпретує рядок як вираз:

"YBa2Cu3O7" -> ""Y""Ba"^2"Cu"^3"O"^7" -> "Y" "Ba"^2 "Cu"^3 "O"^7

Тоді він бере логарифм цього і розширює його (математика не хвилює, що взяти логарифм :)):

Log["Y" "Ba"^2 "Cu"^3 "O"^7] -> Log["Y"] + 2 Log["Ba"] + 3 Log["Cu"] + 7 Log["O"]

а потім він знаходить усі випадки множення a Logна число і аналізує його на форму {log-argument, number}та виводить їх у таблицю. Деякі приклади:

f@"K4(ON(SO3)2)2"
K   4
N   2
O   14
S   4


f@"(CH3)3COOC(CH3)3"
C   8
H   18
O   2


f@"Co3(Fe(CN)6)2"
C   12
Co  3
Fe  2
N   12

1

Java, 827 байт

import java.util.*;class C{String[]x=new String[10];public static void main(String[]a){new C(a[0]);}C(String c){I p=new I();int[]d=d(c,p);for(int i=0;i<10;i++)if(x[i]!=null)System.out.println(x[i]+": "+d[i]);}int[]d(String c,I p){int[]f;int i,j;Vector<int[]>s=new Vector();while(p.v<c.length()){char q=c.charAt(p.v);if(q=='(')s.add(d(c,p.i()));if(q==')')break;if(q>='A'&&q<='Z'){f=new int[10];char[]d=new char[]{c.charAt(p.v),0};i=1;if(c.length()-1>p.v){d[1]=c.charAt(p.v+1);if(d[1]>='a'&&d[1]<='z'){i++;p.i();}}String h=new String(d,0,i);i=0;for(String k:x){if(k==null){x[i]=h;break;}if(k.equals(h))break;i++;}f[i]++;s.add(f);}if(q>='0'&&q<='9'){j=c.charAt(p.v)-'0';f=s.get(s.size()-1);for(i=0;i<10;)f[i++]*=j;}p.i();}f=new int[10];for(int[]w:s){j=0;for(int k:w)f[j++]+=k;}return f;}class I{int v=0;I i(){v++;return this;}}}

Репозиторій Git з джерелом без вольфу (не ідеальний паритет, ungolfed підтримує багатозначні числа).

Невдовзі я подумав, що я дам Яві представлення. Однозначно не вигравати жодної нагороди :).


1

ES6, 198 байт

f=s=>(t=s.replace(/(([A-Z][a-z]?)|\(([A-Za-z]+)\))(\d+)/,(a,b,x,y,z)=>(x||y).repeat(z)))!=s?f(t):(m=new Map,s.match(/[A-Z][a-z]?/g).map(x=>m.set(x,-~m.get(x))),[...m].map(([x,y])=>x+": "+y).join`\n`)

Де \nбуквальний символ нового рядка.

Безголівки:

function f(str) {
    // replace all multiple elements with individual copies
    // then replace all groups with copies working outwards
    while (/([A-Z][a-z]?)(\d+)/.test(str) || /\(([A-Za-z]+)\)(\d+)/.test(str)) {
        str = RegExp.leftContext + RegExp.$1.repeat(RegExp.$2) + RegExp.rightContext;
    }
    // count the number of each element in the expansion
    map = new Map;
    str.match(/[A-Z][a-z]?/g).forEach(function(x) {
        if (!map.has(x)) map.set(x, 1);
        else map.set(x, map.get(x) + 1);
    }
    // convert to string
    res = "";
    map.forEach(function(value, key) {
        res += key + ": " + value + "\n";
    }
    return res;
}

1

Піп , 85 77 + 1 = 78 байт

Неконкурентна відповідь, оскільки вона використовує мовні функції, новіші, ніж виклик. Приймає формулу як аргумент командного рядка та використовує -nпрапор для правильного форматування виводу.

Y(VaRl:`([A-Z][a-z]*)``"&"`R`\d+``X&`R`(?<=\d|")[("]``.&`l)u:UQyu.": ".Y_NyMu

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

Основна хитрість полягає в перетворенні формули за допомогою регекс-замін у вираз Pip. Це, коли eval'd, дозволить повторити і вирішити дужки для нас. Потім ми трохи обробляємо процес, щоб отримати кількість атомів і все правильно відформатувати.

Безголовка, з коментарями:

                         a is command-line arg (implicit)
l:`([A-Z][a-z]*)`        Regex matching element symbols
aR:l `"&"`               Replace each symbol in a with symbol wrapped in quotes
aR:`\d+` `X&`            Add X before each number
aR:`(?<=\d|")[("]` `.&`  Add . before ( or " if it's preceded by a digit or "
Y (Va)@l                 Eval result, findall matches of l, and yank resulting list into y
u:UQy                    Remove duplicates and store in u
u.": ".(_Ny M u)         Map function {a IN y} to u, returning list of element counts;
                           append this (with colon & space) itemwise to list of symbols
                         Print that list, newline-separated (implicit, -n flag)

Ось як Co3(Fe(CN)6)2трансформується вхід :

Co3(Fe(CN)6)2
"Co"3("Fe"("C""N")6)2
"Co"X3("Fe"("C""N")X6)X2
"Co"X3.("Fe".("C"."N")X6)X2
CoCoCoFeCNCNCNCNCNCNFeCNCNCNCNCNCN

Тоді:

["Co" "Co" "Co" "Fe" "C" "N" "C" "N" "C" "N" "C" "N" "C" "N" "C" "N" "Fe" "C" "N" "C" "N" "C" "N" "C" "N" "C" "N" "C" "N"]
["Co" "Fe" "C" "N"]
[3 2 12 12]
["Co: 3" "Fe: 2" "C: 12" "N: 12"]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.