Molekul menjadi Atom

44

Tantangan

Tulis sebuah program yang dapat memecah formula input kimia (lihat di bawah), dan output masing-masing atom dalam bentuk element: atom-count.


Memasukkan

Input sampel:

H2O

Input Anda akan selalu mengandung setidaknya satu elemen, tetapi tidak lebih dari sepuluh. Program Anda harus menerima input yang mengandung tanda kurung, yang dapat disarangkan.

Elemen dalam string akan selalu cocok [A-Z][a-z]*, artinya mereka akan selalu mulai dengan huruf besar. Angka akan selalu satu digit.


Keluaran

Output sampel (untuk input di atas):

H: 2
O: 1

Output Anda secara opsional dapat diikuti oleh baris baru.


Memecah Molekul

Angka di sebelah kanan satu set kurung didistribusikan ke setiap elemen di dalamnya:

Mg(OH)2

Haruskah output:

Mg: 1
O: 2
H: 2

Prinsip yang sama berlaku untuk masing-masing atom:

O2

Haruskah output:

O: 2

Dan juga chaining:

Ba(NO2)2

Haruskah output:

Ba: 1
N: 2
O: 4

Contohnya

> 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

Input dilambangkan dengan tanda panah (lebih dari tanda; >).

Papan angka

Agar skor Anda muncul di papan tulis, itu harus dalam format ini:

# Language, Score

Atau jika Anda mendapat bonus:

# Language, Score (Bytes - Bonus%)

Sunting: Kurung kotak tidak lagi menjadi bagian dari pertanyaan. Jawaban apa pun yang diposting sebelum pukul 3AM UTC, 23 September, aman dan tidak akan terpengaruh oleh perubahan ini.

Zach Gates
sumber
Apa bentuk input yang diizinkan?
Oberon
1
@ZachGates Lebih baik kami diizinkan mendukung, tetapi perlu diingat bahwa kurung siku masih salah. AFAIK dalam rumus kimia kurung siku hanya digunakan untuk menunjukkan konsentrasi. Misalnya: [HCl] = 0.01 mol L^-1.
orlp
Ya, tetapi untuk semua tujuan intensif kita akan menggunakannya untuk pengelompokan juga. @ Atau Kecuali itu benar-benar masalah besar; dalam hal ini, saya akan menghapus tanda kurung sepenuhnya.
Zach Gates
Lihat bagian "Contoh". Adakah sesuatu yang spesifik yang Anda tanyakan? @Oberon Input dilambangkan dengan a >.
Zach Gates
1
Sekedar catatan, contoh-contohnya masih memiliki elemen dengan jumlah atom beberapa digit.
ProgrammerDan

Jawaban:

11

CJam, 59 57 byte

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

Cobalah online di juru bahasa CJam .

Bagaimana itu bekerja

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

Pyth, 66 65 byte

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

Port jawaban Python saya. Hanya mendukung input menggunakan tanda kurung biasa.

orlp
sumber
3
+1. Tiga jawaban dalam satu jam? Bagus.
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))

Hanya mendukung input menggunakan tanda kurung biasa.

Sebelum membuat solusi golf menggunakan di evalatas, saya membuat solusi referensi ini, yang menurut saya sangat elegan:

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

Saya cukup yakin ini bisa dipersingkat, tetapi saya harus kembali bekerja. ;-)

styletron
sumber
2
Saya cukup yakin itu bisa dipersingkat juga. Karena Anda menggunakan ES6, Anda bisa mulai dengan menggunakan notasi panah besar untuk membuat fungsi. Dan returnpernyataan tersirat . Itu sudah cukup untuk saat ini.
Ismael Miguel
Anda juga menggunakan replacebanyak sehingga Anda dapat menyimpan beberapa byte dengan menggunakan xyz[R='replace'](...)waktu pertama dan abc[R] (...)setiap waktu berikutnya.
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)))`)

Cobalah online di sini (semoga tautannya berfungsi, mungkin memerlukan akun online)

Catatan: Jika mencoba online, Anda harus mengganti input()dengan string (mis. "(CH3)3COOC(CH3)3")

Penjelasan

Sage memungkinkan Anda untuk menyederhanakan ekspresi aljabar, asalkan mereka berada dalam format yang tepat (lihat 'manipulasi simbolik' dari tautan ini ). Regex di dalam eval () pada dasarnya berfungsi untuk mendapatkan string input ke dalam format yang tepat, misalnya sesuatu seperti:

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

eval()kemudian akan menyederhanakan ini ke:, 8*C + 18*H + 2*Odan kemudian hanya masalah memformat output dengan substitusi regex lain.

Jarmex
sumber
5

Python 3, 414 byte

Saya harap urutan hasilnya tidak masuk hitungan.

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

Javascript (ES6), 286 284

Tidak jauh lebih pendek dari ES6 yang lain tapi saya berikan yang terbaik. Catatan: ini akan error jika Anda memberikan string kosong atau input yang paling tidak valid. Juga mengharapkan semua grup memiliki hitungan lebih dari 1 (yaitu, tidak CO[OH]). Jika ini melanggar aturan tantangan, beri tahu saya.

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

Menggunakan pendekatan berbasis tumpukan. Pertama, ia memproses string untuk ditambahkan 1ke elemen apa pun tanpa angka, yaitu Co3(Fe(CN)6)2menjadi Co3(Fe1(C1N1)6)2. Kemudian loop melalui urutan terbalik dan mengakumulasikan jumlah elemen.

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

Biola

DankMemes
sumber
5

Perl, 177 172 byte

Kode 171 byte + 1 byte parameter baris perintah

Ok, jadi saya mungkin sedikit terhanyut dengan regex yang satu ini ...

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

Contoh penggunaan:

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

Mathematica, 152 byte

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

Di atas mendefinisikan fungsi fyang mengambil string sebagai input. Fungsi mengambil string dan membungkus setiap nama elemen menjadi tanda kutip dan menambahkan operator eksponensial infiks sebelum setiap angka, kemudian menafsirkan string sebagai ekspresi:

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

Kemudian ia mengambil logaritma itu dan memperluasnya (Mathematica tidak peduli, apa yang harus diambil logaritma :)):

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

dan kemudian ia menemukan semua kemunculan perkalian dari Logdengan angka dan mem-parsingnya ke dalam bentuk {log-argument, number}dan menampilkannya dalam tabel. Beberapa contoh:

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
sumber
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 repositori dengan sumber yang tidak disunat (tidak paritas yang sempurna, ungolfed mendukung angka multi-karakter).

Sudah lama, saya pikir saya akan memberikan Jawa beberapa representasi. Pasti tidak akan memenangkan penghargaan apa pun :).

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

Di mana \nkarakter baris baru literal.

Tidak Disatukan:

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

Pip , 85 77 + 1 = 78 byte

Jawaban tidak bersaing karena menggunakan fitur bahasa yang lebih baru daripada tantangan. Mengambil rumus sebagai argumen baris perintah, dan menggunakan -nbendera untuk pemformatan output yang tepat.

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

Cobalah online!

Trik utamanya adalah mengubah rumus melalui penggantian regex menjadi ekspresi Pip. Ini, ketika dievaluasi, akan melakukan pengulangan dan menyelesaikan tanda kurung untuk kita. Kami kemudian memposting sedikit proses untuk mendapatkan jumlah atom dan memformat semuanya dengan benar.

Tidak disatukan, dengan komentar:

                         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)

Berikut cara input Co3(Fe(CN)6)2ditransformasikan:

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

Kemudian:

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