Cząsteczki do atomów

44

Wyzwanie

Napisz program, który może rozbić wprowadzony wzór chemiczny (patrz poniżej) i wyprowadzić odpowiednie atomy w postaci element: atom-count.


Wejście

Przykładowe dane wejściowe:

H2O

Twój wkład zawsze będzie zawierał co najmniej jeden element, ale nie więcej niż dziesięć. Twój program powinien akceptować dane wejściowe zawierające nawiasy, które mogą być zagnieżdżone.

Elementy w ciągach zawsze będą pasować [A-Z][a-z]*, co oznacza, że ​​zawsze będą zaczynać się od dużej litery. Liczby zawsze będą jednocyfrowe.


Wynik

Przykładowe dane wyjściowe (dla powyższego wejścia):

H: 2
O: 1

Twój wynik może opcjonalnie poprzedzić nowy wiersz.


Rozkład cząsteczek

Liczby po prawej stronie zestawu nawiasów są rozdzielane na każdy element wewnątrz:

Mg(OH)2

Powinien generować:

Mg: 1
O: 2
H: 2

Ta sama zasada dotyczy pojedynczych atomów:

O2

Powinien generować:

O: 2

A także łączenie:

Ba(NO2)2

Powinien generować:

Ba: 1
N: 2
O: 4

Przykłady

> 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

Wejścia są oznaczone strzałką (znak większy niż; >).

Tablica wyników

Twój wynik powinien pojawić się na tablicy, powinien mieć następujący format:

# Language, Score

Lub jeśli zarobiłeś bonus:

# Language, Score (Bytes - Bonus%)

Edycja: nawiasy kwadratowe nie są już częścią pytania. Wszelkie odpowiedzi wysłane przed godziną 3:00 czasu UTC, 23 września, są bezpieczne i nie będą miały wpływu na tę zmianę.

Zach Gates
źródło
Jakie są dozwolone formy wprowadzania danych?
Oberon
1
@ZachGates Lepiej jest, że możemy wspierać, ale pamiętaj, że nawiasy kwadratowe są nadal niepoprawne. AFAIK we wzorach chemicznych w nawiasach kwadratowych stosuje się tylko do wskazanego stężenia. Np [HCl] = 0.01 mol L^-1. :
orlp
Są, ale do wszystkich intensywnych celów wykorzystamy je również do grupowania. @orlp Chyba że to naprawdę wielka sprawa; w takim przypadku usunę całkowicie nawiasy.
Zach Gates
Zobacz sekcję „Przykłady”. Czy pytasz o coś konkretnego? @Oberon Wejścia są oznaczone symbolem >.
Zach Gates
1
Tylko uwaga, przykłady wciąż zawierają elementy z wielocyfrową liczbą atomów.
ProgrammerDan

Odpowiedzi:

11

CJam, 59 57 bajtów

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

Wypróbuj online w interpretatorze CJam .

Jak to działa

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#
Dennis
źródło
10

Pyth, 66 65 bajtów

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

Port mojej odpowiedzi w języku Python. Obsługuje tylko wprowadzanie przy użyciu zwykłych nawiasów.

orlp
źródło
3
+1. Trzy odpowiedzi w ciągu godziny? Miły.
Zach Gates
10

Python3, 157 154 bajtów

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

Obsługuje tylko wprowadzanie przy użyciu zwykłych nawiasów.

Przed stworzeniem powyższego rozwiązania golfowego evalstworzyłem to rozwiązanie referencyjne, które uważam za bardzo eleganckie:

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
źródło
6

JavaScript ES6, 366 bajtów

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/

Jestem pewien, że można to skrócić, ale muszę wrócić do pracy. ;-)

styletron
źródło
2
Jestem pewien, że można go również skrócić. Ponieważ lubisz używać ES6, możesz zacząć od użycia notacji z dużą strzałką do tworzenia funkcji. I niejawne returnoświadczenie. Na razie powinno to wystarczyć.
Ismael Miguel
Używasz również replacedużo, więc możesz zaoszczędzić trochę bajtów, używając xyz[R='replace'](...)pierwszego i abc[R] (...)każdego kolejnego.
DankMemes,
6

SageMath , 156 148 bajtów

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

Wypróbuj online tutaj (mam nadzieję, że link zadziała, może wymagać konta online)

Uwaga: jeśli próbujesz online, musisz zastąpić input()ciągiem (np. "(CH3)3COOC(CH3)3")

Wyjaśnienie

Sage pozwala uprościć wyrażenia algebraiczne, pod warunkiem, że mają odpowiedni format (patrz „manipulacja symboliczna” tego łącza). Wyrazy regularne wewnątrz eval () służą zasadniczo do wprowadzenia ciągu wejściowego do odpowiedniego formatu, na przykład coś takiego:

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

eval()uprości to do:, 8*C + 18*H + 2*Oa następnie wystarczy sformatować dane wyjściowe za pomocą innego podstawienia wyrażenia regularnego.

Jarmex
źródło
5

Python 3, 414 bajtów

Mam nadzieję, że kolejność wyniku się nie liczy.

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
źródło
5

JavaScript (ES6), 286 284

Nie tak dużo krótszy niż drugi ES6, ale dałem z siebie wszystko. Uwaga: spowoduje to błąd, jeśli podasz mu pusty ciąg lub najbardziej nieprawidłowe dane wejściowe. Oczekuje również, że wszystkie grupy będą miały więcej niż 1 (tj. Nie CO[OH]). Jeśli to złamie jakiekolwiek zasady wyzwania, daj mi znać.

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`'))

Stosuje podejście oparte na stosie. Po pierwsze, wstępnie przetwarza ciąg, aby dodać 1do dowolnego elementu bez liczby, tzn . Co3(Fe(CN)6)2Staje się Co3(Fe1(C1N1)6)2. Następnie przechodzi w pętli w odwrotnej kolejności i gromadzi liczbę elementów.

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`
  ')
)

Skrzypce

DankMemes
źródło
5

Perl, 177 172 bajtów

171 bajtów kodu + 1 bajtowy parametr wiersza poleceń

Ok, więc mogłem się trochę ponieść z regexem w tym ...

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

Przykład użycia:

echo "(CH3)3COOC(CH3)3" | perl -p entry.pl
Jarmex
źródło
2

Mathematica, 152 bajty

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

Powyżej definiuje funkcję, fktóra przyjmuje ciąg znaków jako dane wejściowe. Funkcja bierze ciąg i zawija nazwę każdego elementu w cudzysłów i dodaje operator potęgowania wykładnika przed każdą liczbą, a następnie interpretuje ciąg jako wyrażenie:

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

Następnie bierze logarytm tego i rozwija go (mathematica nie dba o to, z czego wziąć logarytm :)):

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

a następnie znajduje wszystkie wystąpienia pomnożenia a Logprzez liczbę i analizuje je w postaci {log-argument, number}i wyświetla te w tabeli. Kilka przykładów:

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
LLAMAMYP
źródło
1

Java, 827 bajtów

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

Repozytorium Git ze źródłami niestosowanymi (nie jest to idealna parzystość, nieskolowane obsługuje liczby wieloznakowe).

Minęło trochę czasu, pomyślałem, że dam reprezentację Java. Zdecydowanie nie wygrywam żadnych nagród :).

ProgrammerDan
źródło
1

ES6, 198 bajtów

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

Gdzie \njest dosłowny znak nowej linii.

Nie golfowany:

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;
}
Neil
źródło
1

Pip , 85 77 + 1 = 78 bajtów

Odpowiedź niekonkurencyjna, ponieważ wykorzystuje funkcje językowe, które są nowsze niż wyzwanie. Pobiera formułę jako argument wiersza polecenia i używa -nflagi do poprawnego formatowania wyjściowego.

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

Wypróbuj online!

Główną sztuczką jest przekształcenie formuły przez zastąpienie wyrażeń regularnych w wyrażenie Pip. To po ewaluacji wykona dla nas powtórzenie i rozwiąże nawiasy. Następnie przetwarzamy trochę później, aby uzyskać liczbę atomów i poprawnie sformatować wszystko.

Niegolfowany, z komentarzami:

                         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)

Oto jak dane wejściowe Co3(Fe(CN)6)2są przekształcane:

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

Następnie:

["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
źródło