分子到原子


44

挑战

编写一个程序,可以分解一个输入化学式(请参见下文),并以形式输出其相应的原子element: atom-count


输入项

输入样例:

H2O

您的输入将始终包含至少一个元素,但不超过十个。您的程序应接受包含括号的输入,该括号可能是嵌套的。

字符串中的元素将始终匹配[A-Z][a-z]*,这意味着它们将始终以大写字母开头。数字将始终为个位数。


输出量

样本输出(用于以上输入):

H: 2
O: 1

您的输出后面可以有换行符。


分解分子

一组括号右边的数字分配给内部的每个元素:

Mg(OH)2

应该输出:

Mg: 1
O: 2
H: 2

相同的原理适用于单个原子:

O2

应该输出:

O: 2

以及链接:

Ba(NO2)2

应该输出:

Ba: 1
N: 2
O: 4

例子

> 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

输入由箭头(大于号;>)表示。

计分板

为了使您的分数出现在黑板上,应该采用以下格式:

# Language, Score

或者,如果您获得了奖金:

# Language, Score (Bytes - Bonus%)

编辑:方括号不再是问题的一部分。在9月23日凌晨3点UTC时间之前发布的所有答案都是安全的,不会受到此更改的影响。


允许的输入形式有哪些?
Oberon 2015年

1
@ZachGates最好允许我们两者之一都支持,但请记住,方括号仍然不正确。化学公式方括号中的AFAIK仅用于指示的浓度。例如:[HCl] = 0.01 mol L^-1
orlp 2015年

它们是,但是出于所有密集目的,我们也将使用它们进行分组。@orlp除非真的很重要 在这种情况下,我将完全去除括号。
扎克·盖茨

请参阅“示例”部分。您有什么要问的具体问题吗?@Oberon输入由表示>
扎克·盖茨

1
仅需注意,示例中仍包含具有多个数字原子计数的元素。
ProgrammerDan

Answers:


11

CJam,59 57字节

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

CJam解释器中在线尝试。

这个怎么运作

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#

10

Pyth,66 65字节

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

我的Python回答的端口。仅支持使用常规括号输入。


3
+1。一个小时内得到三个答案?真好
扎克·盖茨

10

Python3,157个 154字节

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

仅支持使用常规括号输入。

在使用eval上述方法创建高尔夫解决方案之前,我创建了此参考解决方案,发现它非常优雅:

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

6

JavaScript ES6,366字节

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小提琴:https//jsfiddle.net/32tunzkr/1/

我很确定可以将其缩短,但是我需要恢复工作。;-)


2
我很确定它也可以缩短。由于您很喜欢使用ES6,因此可以使用大箭头表示法创建函数。和隐式return声明。现在就足够了。
Ismael Miguel

您还使用replace了很多东西,因此可以通过使用xyz[R='replace'](...)第一次和abc[R] (...)以后的每个时间来节省一些字节。
DankMemes 2015年

6

SageMath156个 148字节的

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

在此处在线尝试(希望链接会起作用,可能需要一个在线帐户)

注意:如果要在线尝试,则需要替换input()为字符串(例如"(CH3)3COOC(CH3)3"

说明

Sage允许您简化代数表达式,只要它们的格式正确(请参阅链接的“符号操纵” )。eval()内部的正则表达式基本上用于将输入字符串转换为正确的格式,例如:

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

eval()然后将其简化为:8*C + 18*H + 2*O,然后只需要用另一个正则表达式替换来格式化输出即可。


5

Python 3,414字节

我希望结果的顺序不计算在内。

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

5

使用Javascript(ES6),286 284

没有比其他ES6短很多,但是我尽了全力。注意:如果您为它提供一个空字符串或大多数无效输入,将会出错。还希望所有组的计数都大于1(即,否CO[OH])。如果这违反了任何挑战规则,请告诉我。

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

使用基于堆栈的方法。首先,它预处理字符串以将其添加1到没有数字的任何元素中,即Co3(Fe(CN)6)2变为Co3(Fe1(C1N1)6)2。然后,它以相反的顺序循环并累积元素计数。

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

小提琴


5

Perl,177 172字节

171字节代码+ 1字节命令行参数

好的,所以我可能对这个正则表达式有些不满意...

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

用法示例:

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

2

Mathematica,152个字节

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

上面定义了一个f将字符串作为输入的函数。该函数采用字符串并将每个元素名称包装在引号中,并在每个数字之前添加一个中缀指数运算符,然后将该字符串解释为一个表达式:

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

然后,它取那个的对数并将其扩展(mathematica不在乎,取什么为:)的对数):

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

然后查找所有与数字相乘的情况,Log并将其解析为形式{log-argument, number}并将其输出到表中。一些例子:

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

1

Java,827个字节

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存储库,带有分类的源代码(不是完美的奇偶校验,未分类的支持多字符数字)。

过了一会儿,我想给Java一些代表。绝对不会赢得任何奖励:)。


1

ES6,198个字节

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

\n文字换行符在哪里。

取消高尔夫:

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

1

85 77 +1 = 78字节

非竞争性答案,因为它使用的语言功能比挑战性要新。将公式用作命令行参数,并使用该-n标志进行正确的输出格式设置。

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

在线尝试!

主要技巧是通过正则表达式替换将公式转换为Pip表达式。评估后,将为我们进行重复并解决括号。然后,我们进行一些后期处理以获得原子计数并正确格式化所有内容。

取消评论,并附有评论:

                         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)

输入Co3(Fe(CN)6)2的转换方式如下:

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

然后:

["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"]
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.