¡Encuentra al señor de un compuesto dado!

12

Desafío

Dada la fórmula de una sustancia química, genera el M r del compuesto.

Ecuación

Cada elemento en el compuesto es seguido por un número que denota el número de dicho átomo en el compuesto. Si no hay un número, solo hay uno de ese átomo en el compuesto.

Algunos ejemplos son:

  • El etanol (C 2 H 6 O) sería C2H6Odonde hay dos átomos de carbono, 6 átomos de hidrógeno y 1 átomo de oxígeno.
  • El hidróxido de magnesio (MgO 2 H 2 ) sería MgO2H2donde hay un átomo de magnesio, dos átomos de oxígeno y dos átomos de hidrógeno.

Tenga en cuenta que nunca tendrá que manejar paréntesis y cada elemento se incluye solo una vez en la fórmula.

Si bien la mayoría de las personas probablemente se apegará al orden con el que se sienta más cómoda, no existe un sistema de pedido estricto. Por ejemplo, el agua puede administrarse como H2Oo OH2.

M r

Nota: Aquí, suponga que la masa de la fórmula es igual a la masa molecular

La MR de un compuesto, la masa molecular, es la suma de los pesos atómicos de los átomos en la molécula.

Los únicos elementos y sus pesos atómicos a 1 decimal que debe soportar (hidrógeno a calcio, sin incluir gases nobles) son los siguientes. También se pueden encontrar aquí.

H  - 1.0      Li - 6.9      Be - 9.0
B  - 10.8     C  - 12.0     N  - 14.0
O  - 16.0     F  - 19.0     Na - 23.0
Mg - 24.3     Al - 27.0     Si - 28.1
P  - 31.0     S  - 32.1     Cl - 35.5
K  - 39.1     Ca - 40.1

Siempre debe dar la salida a un decimal.

Por ejemplo, etanol ( C2H6O) tiene un M r de, 46.0ya que es la suma de los pesos atómicos de los elementos que contiene :

12.0 + 12.0 + 1.0 + 1.0 + 1.0 + 1.0 + 1.0 + 1.0 + 16.0
(2*C + 6*H + 1*O)

Entrada

Una sola cadena en el formato anterior. Puede garantizar que los elementos incluidos en la ecuación serán símbolos elementales reales.

No se garantiza que el compuesto dado exista en la realidad.

Salida

El total de Rm del compuesto, a 1 decimal.

Reglas

Las incorporaciones que tienen acceso a elementos o datos químicos no están permitidas (lo siento, Mathematica)

Ejemplos

Input > Output
CaCO3 > 100.1
H2SO4 > 98.1
SF6 > 146.1
C100H202O53 > 2250.0

Victorioso

El código más corto en bytes gana.

Esta publicación fue adoptada con permiso de caird coinheringaahing . (Publicación ahora eliminada)

Decaimiento Beta
fuente
¿Tenemos que manejar cuantificadores, tales como 2H2O:?
Sr. Xcoder
66
Para los curiosos, esta es la solución de Mathematica (53 bytes):NumberForm[#&@@#~ChemicalData~"MolecularMass",{9,1}]&
JungHwan Min

Respuestas:

6

Jalea , 63 bytes

ḟØDOP%⁽¡ṛị“ÇṚÆ’BH+“Ḳ"ɦṀ⁷6<s¡_-¦y⁼Ḟ¡¡FPɓ‘¤÷5
fØDVȯ1×Ç
Œs>œṗ⁸ḊÇ€S

Un enlace monádico que acepta una lista de caracteres y devuelve un número.

Pruébalo en línea!

¿Cómo?

ḟØDOP%⁽¡ṛị“ÇṚÆ’BH+“Ḳ"ɦṀ⁷6<s¡_-¦y⁼Ḟ¡¡FPɓ‘¤÷5 - Link 1, Atomic weight: list of characters
                                            -                              e.g. "Cl23"
 ØD                                         - digit yield = "0123456789"
ḟ                                           - filter discard                      "Cl"
   O                                        - cast to ordinals                [67,108]
    P                                       - product                            7236
      ⁽¡ṛ                                   - base 250 literal = 1223
     %                                      - modulo                             1121
                                        ¤   - nilad followed by link(s) as a nilad:
          “ÇṚÆ’                             -   base 250 literal  = 983264
               B                            -   convert to binary = [    1,    1,     1,     1,   0,  0,  0,   0, 0,  0,  0, 0,     1,     1,     1, 0, 0,  0,  0,   0]
                H                           -   halve             = [  0.5,  0.5,   0.5,   0.5,   0,  0,  0,   0, 0,  0,  0, 0,   0.5,   0.5,   0.5, 0, 0,  0,  0,   0]
                  “Ḳ"ɦṀ⁷6<s¡_-¦y⁼Ḟ¡¡FPɓ‘    -   code-page indexes = [177  , 34  , 160  , 200  , 135, 54, 60, 115, 0, 95, 45, 5, 121  , 140  , 195  , 0, 0, 70, 80, 155]
                 +                          -   addition          = [177.5, 34.5, 160.5, 200.5, 135, 54, 60, 115, 0, 95, 45, 5, 121.5, 140.5, 195.5, 0, 0, 70, 80, 155]
         ị                                  - index into (1-indexed and modular)
                                            -    ...20 items so e.g. 1121%20=1 so 177.5
                                         ÷5 - divide by 5                          35.5

fØDVȯ1×Ç - Link 2: Total weight of multiple of atoms: list of characters   e.g. "Cl23"
 ØD      - digit yield = "0123456789"
f        - filter keep                                                            "23"
   V     - evaluate as Jelly code                                                  23
    ȯ1   - logical or with one (no digits yields an empty string which evaluates to zero)
       Ç - call last link (1) as a monad (get the atomic weight)                   35.5
      ×  - multiply                                                               816.5

Œs>œṗ⁸ḊÇ€S - Main link: list of characters                             e.g. "C24HCl23"
Œs         - swap case                                                      "c24hcL23"
  >        - greater than? (vectorises)                                      10011000
     ⁸     - chain's left argument                                          "C24HCl23"
   œṗ      - partition at truthy indexes                          ["","C24","H","Cl23"]
      Ḋ    - dequeue                                                 ["C24","H","Cl23"]
       Ç€  - call last link (2) as a monad for €ach                  [  288,  1,  816.5]
         S - sum                                                                 1105.5
Jonathan Allan
fuente
Esta es una de las respuestas de Jelly más largas que he visto, pero todavía tiene menos de la mitad de la duración del programa actualmente en segundo lugar, ¡así que buen trabajo!
Gryphon
6

Python 3 ,  189182168  bytes

-14 bytes utilizando el hash de la respuesta JavaScript de Justin Mariner (ES6) .

import re
lambda s:sum([[9,35.5,39.1,24.3,28.1,14,16,31,40.1,23,32.1,10.8,12,27,6.9,19,0,1][int(a,29)%633%35%18]*int(n or 1)for a,n in re.findall("(\D[a-z]?)(\d*)",s)])

Pruébalo en línea!


A continuación se muestra la versión de 182 bytes, dejaré la explicación para esta: lo anterior solo cambia el orden de los pesos, se usa intpara convertir el nombre del elemento de la base 29y usa diferentes dividendos para comprimir el rango de enteros hacia abajo: vea Justin La respuesta del marinero .

import re
lambda s:sum([[16,31,40.1,32.1,0,24.3,12,39.1,28.1,19,0,9,10.8,23,27,35.5,6.9,14,1][ord(a[0])*ord(a[-1])%1135%98%19]*int(n or 1)for a,n in re.findall("(\D[a-z]?)(\d*)",s)])

Una función sin nombre que acepta una cadena s, y devuelve un número.

Pruébalo en línea!

¿Cómo?

Utiliza una expresión regular para dividir la entrada, sen los elementos y sus cuentas usando:
re.findall("(\D[a-z]?)(\d*)",s)
\Dcoincide exactamente con un no dígito y [a-z]?coincide con 0 o 1 letra minúscula, juntos elementos coincidentes. \d*coincide con 0 o más dígitos. Los paréntesis hacen éstos en dos grupos, y como tal, findall("...",s)devuelve una lista de tuplas de cadenas, [(element, number),...].

El número es fácil de extraer, la única cosa a la manija es que un medio de cadena vacía 1, esto se logra con una lógica orya que las cadenas de Python son Falsey-: int(n or 1).

La cadena de elementos recibe un número único tomando el producto de los ordinales de su primer y último carácter (generalmente son los mismos, por ejemplo, S o C, pero necesitamos diferenciar entre Cl, C, Ca y Na, por lo que no podemos usar uno personaje).

Estos números se dividen en hash para cubrir un rango mucho menor de [0,18], que se encuentra mediante una búsqueda del espacio del módulo que resulta en %1135%98%19. Por ejemplo, "Cl"tiene ordinales 67y 108, que se multiplican para dar 7736, cuál, módulo 1135es 426, qué módulo 98es 34, qué módulo 19es 15; este número se usa para indexar en una lista de enteros: el valor 15 (indexado en 0) en la lista:
[16,31,40.1,32.1,0,24.3,12,39.1,28.1,19,0,9,10.8,23,27,35.5,6.9,14,1]
es 35.5el peso atómico de Cl, que luego se multiplica por el número de tales elementos (como se encontró anteriormente).

Estos productos se agregan juntos usando sum(...).

Jonathan Allan
fuente
Eres un genio ... Me superó por más de 350 bytes
Sr. Xcoder
4

PHP , 235 bytes

preg_match_all("#([A-Z][a-z]?)(\d*)#",$argn,$m);foreach($m[1]as$v)$s+=array_combine([H,Li,Be,B,C,N,O,F,Na,Mg,Al,Si,P,S,Cl,K,Ca],[1,6.9,9,10.8,12,14,16,19,23,24.3,27,28.1,31,32.1,35.5,39.1,40.1])[$v]*($m[2][+$k++]?:1);printf("%.1f",$s);

Pruébalo en línea!

En lugar de array_combine([H,Li,Be,B,C,N,O,F,Na,Mg,Al,Si,P,S,Cl,K,Ca],[1,6.9,9,10.8,12,14,16,19,23,24.3,27,28.1,31,32.1,35.5,39.1,40.1])que pueda usar [H=>1,Li=>6.9,Be=>9,B=>10.8,C=>12,N=>14,O=>16,F=>19,Na=>23,Mg=>24.3,Al=>27,Si=>28.1,P=>31,S=>32.1,Cl=>35.5,K=>39.1,Ca=>40.1]con el mismo recuento de bytes

Jörg Hülsermann
fuente
3

JavaScript (ES6), 150 bytes

c=>c.replace(/(\D[a-z]?)(\d+)?/g,(_,e,n=1)=>s+=[9,35.5,39.1,24.3,28.1,14,16,31,40.1,23,32.1,10.8,12,27,6.9,19,0,1][parseInt(e,29)%633%35%18]*n,s=0)&&s

Inspirado por la respuesta de Python de Jonathan Allan , donde explicó dar a cada elemento un número único y dividir esos números para que estén en un rango más pequeño.

Los elementos se convirtieron en números únicos al interpretarlos como base-29 (0-9 y AS). Luego descubrí que %633%35%18reduce los valores al rango de [0, 17]mientras se mantiene la unicidad.

Fragmento de prueba

f=
c=>c.replace(/(\D[a-z]?)(\d+)?/g,(_,e,n=1)=>s+=[9,35.5,39.1,24.3,28.1,14,16,31,40.1,23,32.1,10.8,12,27,6.9,19,0,1][parseInt(e,29)%633%35%18]*n,s=0)&&s
Input: <input oninput="O.value=f(this.value)"><br>
Result: <input id="O" disabled>

Justin Mariner
fuente
¡Oh, creo que tu camino también me ahorraría unos pocos bytes!
Jonathan Allan
2

Clojure, 198 194 bytes

Actualización: mejor forque reduce.

#(apply +(for[[_ e n](re-seq #"([A-Z][a-z]?)([0-9]*)"%)](*(if(=""n)1(Integer. n))({"H"1"B"10.8"O"16"Mg"24.3"P"31"K"39.1"Li"6.9"C"12"F"19"Al"2"S"32.1"Ca"40.1"Be"9"N"14"Na"23"Si"28.1"Cl"35.5}e))))

Original:

#(reduce(fn[r[_ e n]](+(*(if(=""n)1(Integer. n))({"H"1"B"10.8"O"16"Mg"24.3"P"31"K"39.1"Li"6.9"C"12"F"19"Al"2"S"32.1"Ca"40.1"Be"9"N"14"Na"23"Si"28.1"Cl"35.5}e))r))0(re-seq #"([A-Z][a-z]?)([0-9]*)"%))

Me pregunto si hay una forma más compacta de codificar la tabla de búsqueda.

NikoNyrh
fuente
2

Python 3 , 253 bytes

def m(f,j=0):
 b=j+1
 while'`'<f[b:]<'{':b+=1
 c=b
 while'.'<f[c:]<':':c+=1
 return[6.9,9,23,40.1,24.3,27,28.1,35.5,31,32.1,39.1,1,10.8,12,14,16,19]['Li Be Na Ca Mg Al Si Cl P S K H B C N O F'.split().index(f[j:b])]*int(f[b:c]or 1)+(f[c:]>' 'and m(f,c))

Pruébalo en línea!

ovs
fuente
1

Mathematica, 390 338 329 Bytes

Guarde 9 bytes debido a que ahora estoy despierto y a usar el acortamiento que pretendía.

Versión 2.1:

S=StringSplit;Total[Flatten@{ToExpression@S[#,LetterCharacter],S[#,DigitCharacter]}&/@S[StringInsert[#,".",First/@StringPosition[#,x_/;UpperCaseQ[x]]],"."]/.{"H"->1,"Li"->3,"Be"->9,"B"->10.8,"C"->12,"N"->14,"O"->16,"F"->19,"Na"->23,"Mg"->24.3,"Al"->27,"Si"->28.1,"P"->31,"S"->32.1,"Cl"->35.5,"K"->39.1,"Ca"->40.1}/.{a_,b_}->a*b]&

Explicación: Encuentre la posición de todos los caracteres en mayúscula. Pon un punto antes de cada uno. Divide la cuerda en cada punto. Para esta lista de subcadenas, haga lo siguiente, divídalo en letras y divídalo en dígitos. Para los divididos por letras, convierta la cadena en números. Para los divididos por dígitos, reemplace cada químico con su peso molecular. Para cualquiera con un peso molecular y un conteo de átomos, reemplácelo con el producto de ellos. Ellos encuentran el total.

Versión 1:

Estoy seguro de que esto puede ser mucho golf (o simplemente reescrito por completo). Solo quería descubrir cómo hacerlo. (Se reflexionará sobre ello en la mañana).

F=Flatten;d=DigitCharacter;S=StringSplit;Total@Apply[Times,#,2]&@(Transpose[{F@S[#,d],ToExpression@F@S[#,LetterCharacter]}]&@(#<>If[StringEndsQ[#,d],"","1"]&/@Fold[If[UpperCaseQ[#2],Append[#,#2],F@{Drop[#,-1],Last[#]<>#2}]&,{},StringPartition[#,1]]))/.{"H"->1,"Li"->3,"Be"->9,"B"->10.8,"C"->12,"N"->14,"O"->16,"F"->19,"Na"->23,"Mg"->24.3,"Al"->27,"Si"->28.1,"P"->31,"S"->32.1,"Cl"->35.5,"K"->39.1,"Ca"->40.1}&

Explicación: Primero divide la cadena en caracteres. Luego dobla sobre la matriz uniendo caracteres en minúscula y números de regreso a su capital. A continuación, agregue un 1 a cualquier químico sin un número al final. Luego haga dos divisiones de los términos en la matriz: una división en todos los números y una división en todas las letras. Para el primero, reemplace las letras con sus masas molares y luego encuentre el producto escalar de estas dos listas.

Ian Miller
fuente
1

Python 3 - 408 bytes

Esta es principalmente la solución de @ovs, ya que la redujo en más de 120 bytes ... Vea la solución inicial a continuación.

e='Li Be Na Ca Mg Al Si Cl P S K H B C N O F'.split()
f,g=input(),[]
x=r=0
for i in e:
 if i in f:g+=[(r,eval('6.9 9 23 40.1 24.3 27 28.1 35.5 31 32.1 39.1 1 10.8 12 14 16 19'.split()[e.index(i)]))];f=f.replace(i,' %d- '%r);r+=1
h=f.split()
for c,d in zip(h,h[1:]):
 s=c.find('-')
 if-1<s:
  if'-'in d:
   for y in g:x+=y[1]*(str(y[0])==c[:s])
  else:
   for y in g:x+=y[1]*int(d)*(str(y[0])==c[:s])
print(x)

Pruébalo en línea!

Python 3 - 550 548 535 bytes (perdió la cuenta con sangría)

Guardado 10 bytes gracias a @cairdcoinheringaahing y 3 guardados gracias a ovs

Tenía un objetivo personal de no usar ninguna expresión regular y hacerlo de la manera divertida y tradicional ... Resultó ser 350 bytes más largo que la solución de expresiones regulares, pero solo usa la biblioteca estándar de Python ...

a='Li6.9 Be9. Na23. Ca40.1 Mg24.3 Al27. Si28.1 Cl35.5 P-31. S-32.1 K-39.1 H-1. B-10.8 C-12. N-14. O-16. F-19.'.split()
e,m,f,g,r=[x[:1+(x[1]>'-')]for x in a],[x[2:]for x in a],input(),[],0
for i in e:
 if i in f:g.append((r,float(m[e.index(i)])));f=f.replace(i,' '+str(r)+'- ');r+=1;
h,x=f.split(),0
for i in range(len(h)):
 if '-'in h[i]:
    if '-'in h[i+1]:
     for y in g:x+=y[1]*(str(y[0])==h[i][:h[i].index('-')])
    else:
        for y in g:
         if str(y[0])==h[i][:h[i].index('-')]:x+=(y[1])*int(h[i+1])
 else:1
print(x)  

Pruébalo en línea!


Si alguien está dispuesto a jugar golf (con correcciones de sangría y otros trucos ...), será 100% bien recibido, sintiendo que hay una mejor manera de hacerlo ...

Sr. Xcoder
fuente
Puede reemplazar for y in g: if str(y[0])==h[i][:h[i].index('-')]:x+=y[1]confor y in g:x+=y[1]*(str(y[0])==h[i][:h[i].index('-')])
caird coinheringaahing
@cairdcoinheringaahing ah, genial ... actualización cuando tengo acceso a una computadora
Sr. Xcoder
@ovs Muchas gracias! Te
acredité
En Python, puede usar un punto y coma en lugar de una nueva línea, lo que le permite guardar bytes en la sangría.
Pavel
@ Phoenix no si hay un if/for/whileen la siguiente línea. Como este es el caso en cada línea con sangría, no puede guardar bytes con esto.
ovs