โมเลกุลกับอะตอม


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

แก้ไข:วงเล็บเหลี่ยมไม่ได้เป็นส่วนหนึ่งของคำถามอีกต่อไป คำตอบใด ๆ ที่โพสต์ก่อนเวลา UTC 3AM วันที่ 23 กันยายนปลอดภัยและจะไม่ได้รับผลกระทบจากการเปลี่ยนแปลงนี้


รูปแบบการป้อนข้อมูลที่อนุญาตคืออะไร?
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

พอร์ตของคำตอบ Python ของฉัน รองรับอินพุตโดยใช้วงเล็บทั่วไปเท่านั้น


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

รองรับอินพุตโดยใช้วงเล็บทั่วไปเท่านั้น

ก่อนที่จะสร้างโซลูชัน golfed โดยใช้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 Fiddle: 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 ช่วยให้คุณลดความซับซ้อนของการแสดงออกเกี่ยวกับพีชคณิตโดยให้อยู่ในรูปแบบที่ถูกต้อง (ดู 'การจัดการสัญลักษณ์' ของลิงก์นี้ ) regexes ภายใน eval () จะทำหน้าที่รับสตริงอินพุตในรูปแบบที่ถูกต้องตัวอย่างเช่น:

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

eval()จะทำให้สิ่งนี้ง่ายขึ้นเป็น: 8*C + 18*H + 2*Oแล้วก็เป็นเรื่องของการจัดรูปแบบผลลัพธ์ด้วยการทดแทน regex อื่น


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

ใช้แนวทางแบบกองซ้อน ครั้งแรกก็ preprocesses สตริงเพื่อเพิ่ม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 พารามิเตอร์บรรทัดคำสั่งของไบต์

ตกลงดังนั้นฉันอาจได้ดำเนินไปเล็กน้อยกับ regex ในอันนี้ ...

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ซึ่งใช้สตริงเป็นอินพุต ฟังก์ชั่นใช้สตริงและล้อมชื่อองค์ประกอบแต่ละตัวไว้ในเครื่องหมายคำพูดและเพิ่มตัวดำเนินการยกกำลัง infix ก่อนแต่ละหมายเลขจากนั้นตีความสตริงเป็นนิพจน์:

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

แล้วมันก็จะพบการคูณทั้งหมดของ a 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 repository w / ungolfed source (ไม่ใช่พาริตีที่สมบูรณ์แบบ, 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ตัวอักษรขึ้นบรรทัดใหม่อยู่ที่ไหน

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

1

Pip , 85 77 + 1 = 78 bytes

คำตอบที่ไม่ใช่การแข่งขันเนื่องจากใช้คุณสมบัติภาษาที่ใหม่กว่าความท้าทาย ใช้สูตรเป็นอาร์กิวเมนต์บรรทัดคำสั่งและใช้-nแฟล็กสำหรับการจัดรูปแบบเอาต์พุตที่เหมาะสม

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

ลองออนไลน์!

เคล็ดลับหลักคือการเปลี่ยนสูตรผ่านการแทนที่ regex ให้เป็นนิพจน์ Pip สิ่งนี้เมื่อ eval'd จะทำซ้ำและแก้ไขวงเล็บสำหรับเรา จากนั้นเราประมวลผลเล็กน้อยเพื่อให้ได้จำนวนอะตอมและจัดรูปแบบทุกอย่างถูกต้อง

Ungolfed พร้อมความคิดเห็น:

                         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.