분자에서 원자로


44

도전

입력 화학 공식을 분해 할 수있는 프로그램을 작성하고 (아래 참조) 각 원자를 형식으로 출력하십시오 element: atom-count.


입력

샘플 입력 :

H2O

입력 내용에는 항상 하나 이상의 요소가 포함되지만 10 개 이하가 포함됩니다. 프로그램은 중첩 될 수있는 괄호를 포함하는 입력을 승인해야합니다.

문자열의 요소는 항상 일치 [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

1
@ZachGates 우리는 어느 쪽이든 지원하는 것이 좋지만, 대괄호는 여전히 옳지 않다는 것을 명심하십시오. 화학식의 대괄호 안의 AFAIK는 농도를 나타내는 데만 사용됩니다. 예 : [HCl] = 0.01 mol L^-1.
orlp

그것들은 모든 집중적 인 목적으로 그룹화를 위해 사용할 것입니다. @orlp 정말 큰 문제가 아니라면; 이 경우 대괄호를 완전히 제거합니다.
Zach Gates

"예제"섹션을 참조하십시오. 특정 질문이 있습니까? @Oberon 입력은로 표시됩니다 >.
Zach Gates

1
참고로, 예제에는 여전히 여러 자릿수 원자 수를 가진 요소가 있습니다.
ProgrammerDan

답변:


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

내 파이썬 답변의 포트. 일반 괄호를 사용한 입력 만 지원합니다.


3
+1. 한 시간에 세 가지 대답? 좋은.
Zach Gates

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

자바 스크립트 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

6

SageMath , 156 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

파이썬 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

자바 스크립트 (ES6), 286 284

다른 ES6보다 짧지는 않지만 최선을 다했습니다. 참고 : 빈 문자열이나 가장 잘못된 입력을 주면 오류가 발생합니다. 또한 모든 그룹의 개수가 1보다 클 것으로 예상합니다 (예 : no 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

그런 다음 로그를 가져 와서 확장합니다 (수학은 신경 쓰지 않습니다, 로그를 가져갈 대상 :).

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

자바, 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;}}}

ungolfed 소스가있는 Git 저장소 (완벽하지 않음, ungolfed는 다중 문자 번호 지원)

오랜 시간 동안 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"]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.