分子から原子


44

挑戦

入力化学式を分解し(下記を参照)、それぞれの原子をの形式で出力できるプログラムを作成しelement: atom-countます。


入力

サンプル入力:

H2O

入力には常に少なくとも1つの要素が含まれますが、10個以下です。プログラムは、入れ子になっている可能性のある括弧を含む入力を受け入れる必要があります。

文字列内の要素は常に一致します[A-Z][a-z]*。つまり、常に大文字で始まります。数字は常に1桁です。


出力

サンプル出力(上記の入力用):

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)より前に投稿された回答はすべて安全であり、この変更による影響はありません。


許可される入力形式は何ですか?
オベロン

1
@ZachGatesどちらかをサポートすることは許可されている方が良いですが、角括弧は依然として正しくないことに留意してください。化学式の角括弧内のAFAIKは、濃度を示すためにのみ使用されます。例:[HCl] = 0.01 mol L^-1
orlp

これらは、すべての集中的な目的のために、グループ化にも使用します。@orlpそれが本当に大したことでない限り。その場合、ブラケットを完全に削除します。
ザック・ゲイツ

「例」を参照してください。質問している具体的なものはありますか?@Oberon入力はで示されます>
ザック・ゲイツ

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。1時間で3つの答えですか?いいね
ザック・ゲイツ

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声明。今のところはこれで十分でしょう。
イスマエルミゲル

また、1回目とそれ以降の各時間replaceを使用しxyz[R='replace'](...)ていくつかのバイトを節約できるように、多く使用しますabc[R] (...)
DankMemes

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")に置き換える必要があります

説明

セージを使用すると、数式が正しい形式である限り、代数式を簡素化できます(このリンクの「記号操作」を参照)。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を超える(つまり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バイトのコマンドラインパラメーター

わかりました、それで私はこの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、aと数値の乗算のすべてのオカレンスを検出し、それを形式に解析{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;}}}

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

where \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式に変換することです。これは、評価されると、繰り返しを行い、括弧を解決します。次に、アトムカウントを取得し、すべてを正しくフォーマットするために少し後処理します。

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.