Des molécules aux atomes

44

Le défi

Ecrivez un programme capable de décomposer une formule chimique d'entrée (voir ci-dessous) et de générer ses atomes respectifs sous la forme element: atom-count.


Contribution

Exemple de saisie:

H2O

Votre entrée contiendra toujours au moins un élément, mais pas plus de dix. Votre programme doit accepter les entrées contenant des parenthèses, qui peuvent être imbriquées.

Les éléments dans les chaînes seront toujours identiques [A-Z][a-z]*, ce qui signifie qu'ils commenceront toujours par une lettre majuscule. Les numéros seront toujours des chiffres simples.


Sortie

Exemple de sortie (pour l'entrée ci-dessus):

H: 2
O: 1

Votre sortie peut éventuellement être suivie d'une nouvelle ligne.


Briser des molécules

Les nombres à droite d'un ensemble de parenthèses sont distribués à chaque élément à l'intérieur:

Mg(OH)2

Devrait-il produire:

Mg: 1
O: 2
H: 2

Le même principe s'applique aux atomes individuels:

O2

Devrait-il produire:

O: 2

Et aussi enchaîner:

Ba(NO2)2

Devrait-il produire:

Ba: 1
N: 2
O: 4

Exemples

> 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

Les entrées sont indiquées par une flèche (signe plus grand que; >).

Tableau de bord

Pour que votre score apparaisse au tableau, il devrait être dans ce format:

# Language, Score

Ou si vous avez gagné un bonus:

# Language, Score (Bytes - Bonus%)

Edit: Les crochets ne font plus partie de la question. Toutes les réponses postées avant le 23 septembre, heure de 03h00 (UTC), ne seront pas affectées par ce changement.

Zach Gates
la source
Quelles sont les formes de saisie autorisées?
Oberon
1
@ZachGates Il est préférable que nous soyons autorisés à prendre en charge l'un ou l'autre, mais gardez à l'esprit que les crochets sont toujours incorrects. Dans les formules chimiques, autant que je sache, les crochets ne sont utilisés qu’à la concentration indiquée. Par exemple: [HCl] = 0.01 mol L^-1.
orlp
Ils le sont, mais à des fins intensives, nous allons également les utiliser pour le regroupement. @ orlp Sauf si c'est vraiment un gros problème; dans ce cas, je supprime complètement les crochets.
Zach Gates
Voir la section "Exemples". Vous demandez quelque chose de spécifique? @Oberon Les entrées sont désignées par un >.
Zach Gates
1
Juste une note, les exemples ont toujours des éléments avec un nombre d'atomes de plusieurs chiffres.
ProgrammerDan

Réponses:

11

CJam, 59 57 octets

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

Essayez-le en ligne dans l' interprète CJam .

Comment ça marche

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
la source
10

Pyth, 66 65 octets

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

Réponse de Port of my Python. Ne prend en charge que les entrées utilisant des crochets réguliers.

orlp
la source
3
+1 Trois réponses en une heure? Agréable.
Zach Gates
10

Python3, 157 154 octets

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

Ne prend en charge que les entrées utilisant des crochets réguliers.

Avant de créer la solution golfée en utilisant evalci-dessus, j'ai créé cette solution de référence, que j'ai trouvée très élégante:

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
la source
6

JavaScript ES6, 366 octets

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/

Je suis presque sûr que cela peut être raccourci, mais je dois retourner au travail. ;-)

styletron
la source
2
Je suis à peu près sûr que cela peut aussi être raccourci. Puisque vous clame d'utiliser ES6, vous pouvez commencer par utiliser la notation grosse flèche pour créer des fonctions. Et la returndéclaration implicite . Cela devrait suffire pour le moment.
Ismael Miguel
Vous en utilisez également replacebeaucoup afin de pouvoir économiser des octets en utilisant xyz[R='replace'](...)la première fois et abc[R] (...)chaque fois ultérieure.
DankMemes
6

SageMath , 156 148 octets

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

Essayez-le en ligne ici (espérons que le lien fonctionnera, vous aurez peut-être besoin d'un compte en ligne)

Remarque: Si vous essayez en ligne, vous devrez remplacer input()par la chaîne (par exemple "(CH3)3COOC(CH3)3") .

Explication

Sage vous permet de simplifier les expressions algébriques, à condition qu'elles soient dans le bon format (voir «Manipulation symbolique» de ce lien). Les expressions rationnelles à l'intérieur de eval () servent essentiellement à obtenir la chaîne d'entrée dans le bon format, par exemple quelque chose comme:

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

eval()simplifiera ensuite ceci en:, 8*C + 18*H + 2*Oet ensuite, il s’agira simplement de formater la sortie avec une autre substitution regex.

Jarmex
la source
5

Python 3, 414 octets

J'espère que l'ordre du résultat ne compte pas.

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
la source
5

Javascript (ES6), 286 284

Pas beaucoup plus court que l’autre ES6 mais j’ai donné le meilleur de moi-même. Remarque: ceci provoquera une erreur si vous lui donnez une chaîne vide ou la plupart des entrées non valides. On s'attend également à ce que tous les groupes comptent plus de 1 (c'est-à-dire non CO[OH]). Si cela ne respecte pas les règles du défi, faites le moi savoir.

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

Utilise une approche basée sur la pile. Premièrement, il pré-traite la chaîne pour l'ajouter 1à tout élément sans numéro, c'est-à-dire Co3(Fe(CN)6)2devient Co3(Fe1(C1N1)6)2. Ensuite, il effectue une boucle inversée et accumule le nombre d'éléments.

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

Violon

DankMemes
la source
5

Perl, 177 172 octets

Code de 171 octets + paramètre de ligne de commande d'octet 1 octet

Ok, alors je me suis peut-être un peu laissé emporter par les regex de celui-ci ...

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

Exemple d'utilisation:

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

Mathematica, 152 octets

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

Ce qui précède définit une fonction fqui prend une chaîne en entrée. La fonction prend la chaîne et encapsule chaque nom d'élément entre guillemets et ajoute un opérateur d'exponentiation infixe avant chaque nombre, puis interprète la chaîne comme une expression:

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

Ensuite, il prend le logarithme de cela et le développe (mathematica se fiche de quoi prendre le logarithme de :)):

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

et puis il trouve toutes les occurrences de multiplication de a Logpar un nombre et les analyse sous la forme de {log-argument, number}et les affiche sous forme de tableau. Quelques exemples:

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
la source
1

Java, 827 octets

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

Dépôt Git avec source non- golfée (pas de parité parfaite, ungolfed supporte les nombres multi-caractères).

Ça fait longtemps que je donnerais une représentation à Java. Ne va certainement pas gagner de prix :).

ProgrammeurDan
la source
1

ES6, 198 octets

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

\nest un caractère de nouvelle ligne littéral.

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;
}
Neil
la source
1

Pip , 85 77 + 1 = 78 octets

Réponse non concurrente car elle utilise des fonctionnalités linguistiques plus récentes que le défi. Prend la formule en tant qu'argument de ligne de commande et utilise l' -nindicateur pour un formatage de sortie correct.

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

Essayez-le en ligne!

L'astuce principale consiste à transformer la formule via les substitutions de regex en une expression Pip. Ceci, une fois évalué, fera la répétition et résoudra les parenthèses pour nous. Nous post-traitons ensuite un peu pour obtenir le nombre d'atomes et formater tout correctement.

Ungolfed, avec des commentaires:

                         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)

Voici comment l'entrée Co3(Fe(CN)6)2est transformée:

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

Ensuite:

["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
la source