Молекулы к атомам

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 Если это действительно большое дело; в этом случае я полностью уберу скобки.
Зак Гейтс
Смотрите раздел «Примеры». Есть что-то конкретное, о чем ты спрашиваешь? @ Оберон Входы обозначены >.
Зак Гейтс
1
Просто обратите внимание, что в примерах все еще есть элементы с несколькими цифрами числа атомов.
ProgrammerDan

Ответы:

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

orlp
источник
3
+1. Три ответа в час? Ницца.
Зак Гейтс
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))
orlp
источник
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/

Я почти уверен, что это можно сократить, но мне нужно вернуться к работе. ;-)

styletron
источник
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и тогда это просто вопрос форматирования вывода с другой подстановкой регулярного выражения.

Jarmex
источник
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]))
uno20001
источник
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`
  ')
)

скрипка

DankMemes
источник
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
Jarmex
источник
2

Mathematica, 152 байта

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

Выше определяется функция, fкоторая принимает строку в качестве входных данных. Функция берет строку и упаковывает каждое имя элемента в кавычки и добавляет оператор возведения в число infix перед каждым числом, затем интерпретирует строку как выражение:

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

Затем он берет логарифм этого и расширяет его (mathematica не волнует, что взять логарифм :)):

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
LLlAMnYP
источник
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 поддерживает многосимвольные числа).

Давным-давно решил, что я дам представление Java. Определенно не собираюсь выигрывать какие-либо награды :).

ProgrammerDan
источник
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буквальный символ новой строки.

Ungolfed:

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

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

Основная хитрость заключается в том, чтобы преобразовать формулу с помощью замены регулярных выражений в выражение Пипа. Это, когда eval'd, сделает повторение и разрешит скобки для нас. Затем мы немного постобработаем, чтобы получить количество атомов и отформатировать все правильно.

Ungolfed, с комментариями:

                         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"]
DLosc
источник