Moleküle zu Atomen

44

Die Herausforderung

Schreiben Sie ein Programm, das eine eingegebene chemische Formel aufschlüsselt (siehe unten) und die entsprechenden Atome in der Form ausgibt element: atom-count.


Eingang

Beispieleingabe:

H2O

Ihre Eingabe enthält immer mindestens ein Element, jedoch nicht mehr als zehn. Ihr Programm sollte Eingaben akzeptieren, die geschachtelte Klammern enthalten.

Elemente in den Zeichenfolgen stimmen immer überein [A-Z][a-z]*, dh sie beginnen immer mit einem Großbuchstaben. Zahlen sind immer einstellige Zahlen.


Ausgabe

Beispielausgabe (für die obige Eingabe):

H: 2
O: 1

Ihrer Ausgabe kann optional eine neue Zeile folgen.


Moleküle abbauen

Die Zahlen rechts von einer Reihe von Klammern werden auf jedes Element innerhalb von verteilt:

Mg(OH)2

Sollte ausgeben:

Mg: 1
O: 2
H: 2

Das gleiche Prinzip gilt für einzelne Atome:

O2

Sollte ausgeben:

O: 2

Und auch Verketten:

Ba(NO2)2

Sollte ausgeben:

Ba: 1
N: 2
O: 4

Beispiele

> 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

Eingaben werden durch einen Pfeil (Vorzeichen; >) gekennzeichnet.

Anzeigetafel

Damit Ihre Partitur auf der Tafel erscheint, sollte sie in folgendem Format vorliegen:

# Language, Score

Oder wenn Sie einen Bonus verdient haben:

# Language, Score (Bytes - Bonus%)

Bearbeiten: Eckige Klammern sind nicht mehr Teil der Frage. Alle Antworten, die vor dem 23. September, 3:00 Uhr UTC, eingehen, sind sicher und werden von dieser Änderung nicht betroffen.

Zach Gates
quelle
Was sind die erlaubten Eingabeformen?
Oberon
1
@ZachGates Es ist besser, dass wir beide unterstützen dürfen, aber denken Sie daran, dass eckige Klammern immer noch falsch sind. AFAIK in chemischen Formeln eckige Klammern werden nur zur Angabe der Konzentration verwendet. Zum Beispiel: [HCl] = 0.01 mol L^-1.
Orlp
Sie sind es, aber für alle intensiven Zwecke werden wir sie auch zum Gruppieren verwenden. @orlp Es sei denn, es ist wirklich eine große Sache; In diesem Fall entferne ich die Klammern vollständig.
Zach Gates
Siehe den Abschnitt "Beispiele". Gibt es etwas Bestimmtes, nach dem Sie fragen? @Oberon-Eingänge sind mit a gekennzeichnet >.
Zach Gates
1
Nur eine Anmerkung, die Beispiele enthalten immer noch Elemente mit mehrstelligen Atomzahlen.
ProgrammerDan

Antworten:

11

CJam, 59 57 Bytes

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

Probieren Sie es online im CJam-Interpreter aus .

Wie es funktioniert

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

Pyth, 66 65 Bytes

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

Port meiner Python-Antwort. Unterstützt nur die Eingabe in regulären Klammern.

orlp
quelle
3
+1. Drei Antworten in einer Stunde? Nett.
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))

Unterstützt nur die Eingabe in regulären Klammern.

Vor dem Erstellen der Golflösung mithilfe der evalobigen Informationen habe ich diese Referenzlösung erstellt, die ich als sehr elegant empfand :

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
quelle
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/

Ich bin mir ziemlich sicher, dass das gekürzt werden kann, aber ich muss mich wieder an die Arbeit machen. ;-)

Styletron
quelle
2
Ich bin mir ziemlich sicher, dass es auch gekürzt werden kann. Da Sie sich für die Verwendung von ES6 entschieden haben, können Sie zunächst mithilfe der Big-Arrow-Notation Funktionen erstellen. Und die implizite returnAussage. Das sollte vorerst reichen.
Ismael Miguel
Sie verwenden auch replaceviel, um einige Bytes zu sparen, indem Sie xyz[R='replace'](...)das erste Mal und abc[R] (...)jedes weitere Mal verwenden.
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)))`)

Probieren Sie es hier online aus (hoffentlich funktioniert der Link, möglicherweise ist ein Online-Account erforderlich)

Hinweis: Wenn Sie es online versuchen, müssen Sie es durch input()die Zeichenfolge ersetzen (z. B. "(CH3)3COOC(CH3)3")

Erläuterung

Mit Sage können Sie algebraische Ausdrücke vereinfachen, sofern sie im richtigen Format vorliegen (siehe 'Symbolische Manipulation' dieses Links). Die regulären Ausdrücke in eval () dienen im Wesentlichen dazu, die Eingabezeichenfolge in das richtige Format zu bringen, zum Beispiel in etwa wie folgt:

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

eval()wird dies dann zu vereinfachen 8*C + 18*H + 2*O:, und dann ist es nur eine Frage der Formatierung der Ausgabe mit einer anderen Regex-Ersetzung.

Jarmex
quelle
5

Python 3, 414 Bytes

Ich hoffe, dass die Reihenfolge des Ergebnisses nicht zählt.

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

Javascript (ES6), 286 284

Nicht viel kürzer als die andere ES6, aber ich habe mein Bestes gegeben. Hinweis: Dies wird fehlschlagen, wenn Sie eine leere Zeichenfolge oder die meisten ungültigen Eingaben eingeben. Erwartet auch, dass alle Gruppen eine Anzahl von mehr als 1 haben (dh nein CO[OH]). Wenn dies gegen die Herausforderungsregeln verstößt, lassen Sie es mich wissen.

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

Verwendet einen stapelbasierten Ansatz. Zunächst wird die Zeichenfolge vorverarbeitet, um sie 1zu jedem Element ohne Zahl hinzuzufügen , dh Co3(Fe(CN)6)2wird Co3(Fe1(C1N1)6)2. Dann durchläuft es in umgekehrter Reihenfolge und sammelt die Anzahl der Elemente.

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

Geige

DankMemes
quelle
5

Perl, 177 172 Bytes

171 Byte Code + 1 Byte Befehlszeilenparameter

Ok, vielleicht habe ich mich in diesem Fall ein bisschen von Regex mitreißen lassen ...

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

Anwendungsbeispiel:

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

Mathematica, 152 Bytes

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

Das Obige definiert eine Funktion, fdie einen String als Eingabe akzeptiert. Die Funktion verwendet die Zeichenfolge, umschließt jeden Elementnamen mit Anführungszeichen und fügt vor jeder Zahl einen Infix-Exponentiationsoperator hinzu. Die Zeichenfolge wird dann als Ausdruck interpretiert:

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

Dann nimmt es den Logarithmus davon und erweitert ihn (Mathematica ist es egal, was man den Logarithmus von nimmt :)):

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

und dann findet es alle Vorkommen der Multiplikation von a Logmit einer Zahl und analysiert sie in die Form von {log-argument, number}und gibt diese in einer Tabelle aus. Einige Beispiele:

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

Git-Repository mit ungolfed-Quelle (keine perfekte Parität, ungolfed unterstützt Zahlen mit mehreren Zeichen).

Ich dachte mir schon eine Weile, ich würde Java eine Vorstellung geben. Ich werde definitiv keine Preise gewinnen :).

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

Wo \nist ein buchstäbliches Newline-Zeichen.

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

Pip , 85 77 + 1 = 78 Bytes

Nicht konkurrierende Antwort, da Sprachfunktionen verwendet werden, die neuer sind als die Herausforderung. Nimmt die Formel als Befehlszeilenargument und verwendet das -nFlag für die richtige Ausgabeformatierung.

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

Probieren Sie es online!

Der Haupttrick besteht darin, die Formel über reguläre Ausdrücke in einen Pip-Ausdruck umzuwandeln. Dies wird, wenn es evaluiert wird, die Wiederholung durchführen und Klammern für uns auflösen. Anschließend werden die Atomzahlen ein wenig nachbearbeitet und alles korrekt formatiert.

Ungolfed, mit Kommentaren:

                         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)

So wird die Eingabe Co3(Fe(CN)6)2transformiert:

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

Dann:

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