Moléculas a los átomos

44

El reto

Escriba un programa que pueda descomponer una fórmula química de entrada (ver más abajo) y generar sus respectivos átomos en la forma element: atom-count.


Entrada

Entrada de muestra:

H2O

Su entrada siempre contendrá al menos un elemento, pero no más de diez. Su programa debe aceptar entradas que contengan paréntesis, que pueden estar anidadas.

Los elementos en las cadenas siempre coincidirán [A-Z][a-z]*, lo que significa que siempre comenzarán con una letra mayúscula. Los números siempre serán de un solo dígito.


Salida

Salida de muestra (para la entrada anterior):

H: 2
O: 1

Su salida puede ser opcionalmente seguida por una nueva línea.


Descomponiendo Moléculas

Los números a la derecha de un conjunto de paréntesis se distribuyen a cada elemento dentro:

Mg(OH)2

Debería dar salida:

Mg: 1
O: 2
H: 2

El mismo principio se aplica a los átomos individuales:

O2

Debería dar salida:

O: 2

Y también encadenando:

Ba(NO2)2

Debería dar salida:

Ba: 1
N: 2
O: 4

Ejemplos

> 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

Las entradas se denotan con una flecha (signo mayor que >) ;

Marcador

Para que su puntaje aparezca en el tablero, debe estar en este formato:

# Language, Score

O si ganaste un bono:

# Language, Score (Bytes - Bonus%)

Editar: Los corchetes ya no son parte de la pregunta. Cualquier respuesta publicada antes de las 3 a.m., hora UTC del 23 de septiembre, es segura y no se verá afectada por este cambio.

Puertas de Zach
fuente
¿Cuáles son las formas de entrada permitidas?
Oberon
1
@ZachGates Es mejor que se nos permita admitir, pero tenga en cuenta que los corchetes siguen siendo incorrectos. AFAIK en fórmulas químicas los corchetes solo se usan para la concentración indicada. Por ejemplo: [HCl] = 0.01 mol L^-1.
orlp
Lo son, pero para todos los propósitos intensivos también los usaremos para agrupar. @orlp A menos que sea realmente un gran problema; en cuyo caso, eliminaré los corchetes por completo.
Zach Gates
Vea la sección "Ejemplos". ¿Hay algo específico sobre lo que estás preguntando? Las entradas de @Oberon se denotan con a >.
Zach Gates
1
Solo una nota, los ejemplos todavía tienen elementos con recuentos de átomos de varios dígitos.
ProgramadorDan

Respuestas:

11

CJam, 59 57 bytes

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

Pruébelo en línea en el intérprete de CJam .

Cómo funciona

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
fuente
10

Pyth, 66 65 bytes

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

Puerto de mi respuesta de Python. Solo admite entrada con corchetes regulares.

orlp
fuente
3
+1. ¿Tres respuestas en una hora? Agradable.
Zach Gates
10

Python3, 157 154 bytes

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

Solo admite entrada con corchetes regulares.

Antes de crear la solución de golf usando lo evalanterior, creé esta solución de referencia, que me pareció muy 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))
orlp
fuente
6

JavaScript ES6, 366 bytes

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/

Estoy bastante seguro de que esto se puede acortar, pero necesito volver al trabajo. ;-)

styletron
fuente
2
Estoy bastante seguro de que también se puede acortar. Dado que afirma que está usando ES6, puede comenzar usando la notación de flecha grande para crear funciones. Y la returndeclaración implícita . Eso debería ser suficiente por ahora.
Ismael Miguel
También usa replacemucho para poder guardar algunos bytes usando xyz[R='replace'](...)la primera vez y abc[R] (...)cada vez posterior.
DankMemes
6

SageMath , 156 148 bytes

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

Pruébelo en línea aquí (con suerte el enlace funcionará, podría necesitar una cuenta en línea)

Nota: Si intenta en línea, deberá reemplazarlo input()con la cadena (por ejemplo "(CH3)3COOC(CH3)3")

Explicación

Sage le permite simplificar expresiones algebraicas, siempre que estén en el formato correcto (vea 'manipulación simbólica' de este enlace). Las expresiones regulares dentro de eval () básicamente sirven para obtener la cadena de entrada en el formato correcto, por ejemplo algo como:

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

eval()luego simplificará esto a:, 8*C + 18*H + 2*Oy luego es solo cuestión de formatear la salida con otra sustitución de expresiones regulares.

Jarmex
fuente
5

Python 3, 414 bytes

Espero que el orden del resultado no cuente.

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
fuente
5

Javascript (ES6), 286 284

No es mucho más corto que el otro ES6, pero di lo mejor de mí. Nota: esto generará un error si le da una cadena vacía o la mayoría de las entradas no válidas. También espera que todos los grupos tengan un recuento de más de 1 (es decir, no CO[OH]). Si esto rompe alguna regla de desafío, avíseme.

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

Utiliza un enfoque basado en la pila. Primero, procesa previamente la cadena para agregar 1a cualquier elemento sin un número, es decir, se Co3(Fe(CN)6)2convierte Co3(Fe1(C1N1)6)2. Luego se repite en orden inverso y acumula recuentos de elementos.

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

Violín

DankMemes
fuente
5

Perl, 177 172 bytes

Código de 171 bytes + parámetro de línea de comando de 1 byte

Ok, puede que me haya dejado llevar un poco de expresión regular en este ...

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

Ejemplo de uso:

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

Mathematica, 152 bytes

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

Lo anterior define una función fque toma una cadena como entrada. La función toma la cadena y ajusta el nombre de cada elemento entre comillas y agrega un operador de exponenciación infijo antes de cada número, luego interpreta la cadena como una expresión:

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

Luego toma el logaritmo de eso y lo expande (a Mathica no le importa, qué tomar el logaritmo de :)):

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

y luego encuentra todas las apariciones de multiplicación de a Logpor un número y lo analiza en forma de {log-argument, number}y genera los de una tabla. Algunos ejemplos:

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
fuente
1

Java, 827 bytes

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

Repositorio de Git con fuente no oculta (paridad no perfecta, no compatible admite números de varios caracteres).

Ha pasado un tiempo, pensé que le daría alguna representación a Java. Definitivamente no voy a ganar ningún premio :).

Programador Dan
fuente
1

ES6, 198 bytes

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

Donde \nes un carácter literal de nueva línea.

Sin golf:

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
fuente
1

Pip , 85 77 + 1 = 78 bytes

Respuesta no competitiva porque usa características de lenguaje que son más nuevas que el desafío. Toma la fórmula como un argumento de línea de comandos y usa el -nindicador para un formato de salida adecuado.

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

Pruébalo en línea!

El truco principal es transformar la fórmula mediante reemplazos de expresiones regulares en una expresión Pip. Esto, cuando evaluado, hará la repetición y resolverá los paréntesis por nosotros. Luego procesamos un poco para obtener los recuentos de átomos y formatear todo correctamente.

Sin golf, con comentarios:

                         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)

Así es como Co3(Fe(CN)6)2se transforma la entrada :

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

Entonces:

["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
fuente