Molecole agli atomi


44

La sfida

Scrivi un programma che può scomporre una formula chimica di input (vedi sotto) e produrre i suoi rispettivi atomi nel modulo element: atom-count.


Ingresso

Input di esempio:

H2O

Il tuo input conterrà sempre almeno un elemento, ma non più di dieci. Il programma dovrebbe accettare input che contengono parentesi, che possono essere nidificate.

Gli elementi nelle stringhe corrisponderanno sempre [A-Z][a-z]*, il che significa che inizieranno sempre con una lettera maiuscola. I numeri saranno sempre a singola cifra.


Produzione

Esempio di output (per l'ingresso sopra):

H: 2
O: 1

L'output può essere facoltativamente seguito da una nuova riga.


Abbattere le molecole

I numeri a destra di una serie di parentesi sono distribuiti a ciascun elemento all'interno:

Mg(OH)2

Dovrebbe produrre:

Mg: 1
O: 2
H: 2

Lo stesso principio si applica ai singoli atomi:

O2

Dovrebbe produrre:

O: 2

E anche concatenare:

Ba(NO2)2

Dovrebbe produrre:

Ba: 1
N: 2
O: 4

Esempi

> 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

Gli input sono indicati da una freccia (segno maggiore di; >).

tabellone segnapunti

Affinché il tuo punteggio appaia sul tabellone, dovrebbe essere in questo formato:

# Language, Score

O se hai guadagnato un bonus:

# Language, Score (Bytes - Bonus%)

Modifica: le parentesi quadre non fanno più parte della domanda. Tutte le risposte postate prima delle 3:00 UTC, il 23 settembre, sono sicure e non saranno interessate da questa modifica.


Quali sono le forme di input consentite?
Oberon,

1
@ZachGates È meglio che ci sia consentito supportare entrambi, ma tieni presente che le parentesi quadre sono ancora errate. Le parentesi quadre AFAIK nelle formule chimiche sono utilizzate solo per la concentrazione indicata. Ad esempio: [HCl] = 0.01 mol L^-1.
orlp,

Lo sono, ma per tutti gli scopi intensivi li useremo anche per il raggruppamento. @orlp A meno che non sia davvero un grosso problema; in tal caso, rimuoverò completamente le parentesi.
Zach Gates,

Vedi la sezione "Esempi". C'è qualcosa di specifico che stai chiedendo? Gli ingressi @Oberon sono indicati con a >.
Zach Gates,

1
Solo una nota, gli esempi hanno ancora elementi con conteggi di atomi a più cifre.
Programmatore:

Risposte:


11

CJam, 59 57 byte

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

Provalo online nell'interprete CJam .

Come funziona

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 byte

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

Port of my Python answer. Supporta solo input usando parentesi regolari.


3
+1. Tre risposte in un'ora? Bello.
Zach Gates,

10

Python3, 157 154 byte

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

Supporta solo input usando parentesi regolari.

Prima di creare la soluzione golfizzata usando evalsopra ho creato questa soluzione di riferimento, che ho trovato molto elegante:

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 byte

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/

Sono abbastanza sicuro che questo possa essere abbreviato, ma devo tornare al lavoro. ;-)


2
Sono abbastanza sicuro che può anche essere abbreviato. Dato che ti impegni a utilizzare ES6, puoi iniziare utilizzando la notazione con freccia grande per creare funzioni. E la returndichiarazione implicita . Questo dovrebbe essere abbastanza per ora.
Ismael Miguel,

Si utilizza anche replacemolto in modo da poter salvare alcuni byte utilizzando xyz[R='replace'](...)la prima volta e abc[R] (...)ogni volta successiva.
DankMemes,

6

SageMath , 156 148 byte

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

Provalo online qui (speriamo che il link funzioni, potrebbe essere necessario un account online)

Nota: se provi online, dovrai sostituirlo input()con la stringa (ad es. "(CH3)3COOC(CH3)3")

Spiegazione

Sage ti consente di semplificare le espressioni algebriche, purché siano nel formato giusto (vedi "manipolazione simbolica" di questo link). Le regex all'interno di eval () servono essenzialmente per ottenere la stringa di input nel formato giusto, ad esempio qualcosa di simile:

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

eval()semplifica quindi questo a:, 8*C + 18*H + 2*Oe quindi è solo una questione di formattazione dell'output con un'altra sostituzione regex.


5

Python 3, 414 byte

Spero che l'ordine del risultato non conti.

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

Non molto più corto dell'altro ES6, ma ho dato il massimo. Nota: questo errore verrà emesso se si fornisce una stringa vuota o la maggior parte degli input non validi. Si aspetta inoltre che tutti i gruppi abbiano un conteggio superiore a 1 (ovvero, no CO[OH]). Se questo infrange le regole della sfida, fammi sapere.

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

Utilizza un approccio basato su stack. Innanzitutto, preelabora la stringa da aggiungere 1a qualsiasi elemento senza un numero, ovvero Co3(Fe(CN)6)2diventa Co3(Fe1(C1N1)6)2. Quindi scorre in ordine inverso e accumula il conteggio degli elementi.

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

Violino


5

Perl, 177 172 byte

Codice 171 byte + parametro riga comandi 1 byte

Ok, quindi potrei essermi un po ' lasciato trasportare con regex su questo ...

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

Esempio di utilizzo:

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

2

Mathematica, 152 byte

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

Quanto sopra definisce una funzione fche accetta una stringa come input. La funzione prende la stringa e racchiude il nome di ciascun elemento tra virgolette e aggiunge un operatore di esponenziale infisso prima di ogni numero, quindi interpreta la stringa come espressione:

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

Quindi prende il logaritmo di quello e lo espande (a matematica non importa, cosa prendere il logaritmo di :)):

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

e quindi trova tutte le occorrenze della moltiplicazione di a Logper un numero e lo analizza nella forma di {log-argument, number}e genera quelli in una tabella. Qualche esempio:

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 byte

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 repository w / sorgente non golfizzata (non perfetta parità, non golfata supporta numeri multi-carattere).

Sono stato un po ', ho pensato che avrei dato a Java una rappresentazione. Sicuramente non vincerà alcun premio :).


1

ES6, 198 byte

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

Dov'è \nun personaggio letterale newline.

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

Pip , 85 77 + 1 = 78 byte

Risposta non competitiva perché utilizza funzionalità linguistiche più recenti della sfida. Prende la formula come argomento della riga di comando e utilizza il -nflag per una corretta formattazione dell'output.

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

Provalo online!

Il trucco principale è trasformare la formula tramite sostituzioni regex in un'espressione Pip. Questo, quando valutato, farà la ripetizione e risolverà le parentesi per noi. Quindi post-processiamo un po 'per ottenere i conteggi degli atomi e formattare tutto correttamente.

Ungolfed, con commenti:

                         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)

Ecco come Co3(Fe(CN)6)2viene trasformato l'input :

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

Poi:

["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"]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.