Молярные массы соединений

14

задача

Напишите программу, которая принимает соединение, состоящее исключительно из элементов с атомным номером, меньшим или равным 92 (уран), и выводит молярную массу соединения в grams/mole.

Правила и ограничения

  • Вы не можете использовать функцию, которая напрямую рассчитывает молярную массу для вас.
  • Ваша программа должна быть в состоянии работать на автономном компьютере.
  • Вы МОЖЕТЕ использовать отдельный файл данных. Содержимое этого файла должно быть предоставлено.
  • Ваша оценка - это длина вашей программы в байтах, плюс длина вашего файла данных в байтах, если вы решите ее использовать.
  • Это , поэтому выигрывает самый низкий балл.

вход

Строка, содержащая соединение. Эта строка может быть прочитан через STDIN, передается в качестве аргумента, или установить переменную (просьба указать , какие). Ниже приведены все действительные данные:

  • CH2 (один углерод, два водорода)
  • (CH2)8 (8 атомов углерода, 16 атомов водорода)
  • U (1 уран)

Ваша программа не обязана функционировать для встроенных скобок (то есть ((CH3)2N4)4), а также для любого соединения, которое содержит элемент с атомным номером больше 92. Если ваша программа не работает ни в одном из двух вышеупомянутых случаев, она должна вывести Invalid formulaдля таких случаев - нет неопределенного поведения.

Выход

Вы должны вывести, в STDOUTили в переменную, молярную массу соединения в grams/mole. Список атомных масс элементов доступен здесь (Википедия) . Ваш ответ должен быть точным с точностью до десятичного знака для соединений, содержащих менее 100 атомов (округлено).

Если ввод неверен, Invalid formulaдолжен быть выведен.

Информация о расчете молярной массы соединения в grams/mole(Википедия).

Примеры

Input                   Output
H2O                     18.015
O                       15.999 (16 acceptable)
C6H2(NO2)3CH3           227.132
FOOF                    69.995
C6H12Op3                Invalid formula
Np                      237 (or Invalid formula)
((C)3)4                 144.132 (or Invalid formula)
CodeGolf                Invalid formula
es1024
источник
1
«до десятичной точки» - для скольких молекул? Массу U1000000в десятичной запятой определить труднее, чем массуU2
Джон Дворжак
до десятичной точки для молекул с менее чем 100 атомами. Добавил это в постановку вопроса.
es1024
Я также предполагаю, что мне разрешено выводить, 13если атомная масса равна 12.999?
Джон Дворжак
1
Это не звучит правильно @ es1024. 13, хорошо, но 12?
RubberDuck
1
«Ваш ответ должен быть точным с точностью до десятичного знака» означает ли это одно десятичное число или ближайшее целое число?

Ответы:

5

Bash, 978 708 675 673 650 636 632 631 598 594

211 байт для программы и 382 байт для данных.

Вход на STDIN, выход на STDOUT. ВНИМАНИЕ: создает вызываемые файлы gи a, если они существуют, они будут перезаписаны!

zcat y>g 2>a
s=(`sed 's/[A-Z][a-z]*/& /g;q'<g`)
p=1.008
v=`sed 's/[0-9]\+/*&/g;s/(/+(0/g'`
r=(`tail -1 g`)
for n in {0..91};{
p=`bc<<<$p+${r[n]}`
v=${v//${s[n]}/+$p}
}
o=`bc<<<0$v 2>a`
echo ${o:-Invalid formula}

Файл данных

Для этого требуется файл с именем y, который является zopfli- сжатой формой этих данных (без завершающей строки). zopfli является gzip-совместимым алгоритмом сжатия и может быть распакован стандартными инструментами gzip. Он был запущен с 1024 итерациями (это, вероятно, слишком много). Последние 8 байтов были удалены.

HHeLiBeBCNOFNeNaMgAlSiPSClKArCaScTiVCrMnFeNiCoCuZnGaGeAsSeBrKrRbSrYZrNbMoTcRuRhPdAgCdInSnSbITeXeCsBaLaCePrNdPmSmEuGdTbDyHoErTmYbLuHfTaWReOsIrPtAuHgTlPbBiPoAtRnFrRaAcPaThU
0 2.995 2.937 2.072 1.798 1.201 1.996 1.992 2.999 1.182 2.81 1.315 2.677 1.103 2.889 1.086 3.39 3.648 .850 .130 4.878 2.911 3.075 1.054 2.942 .907 2.848 .24 4.613 1.834 4.346 2.904 2.292 4.038 .944 3.894 1.670 2.152 1.286 2.318 1.682 3.054 2.04 3.07 1.836 3.514 1.448 4.543 2.407 3.892 3.050 5.144 .696 3.693 1.612 4.422 1.578 1.211 .792 3.334 .758 5.36 1.604 5.286 1.675 3.575 2.43 2.329 1.675 4.120 1.913 3.523 2.458 2.892 2.367 4.023 1.987 2.867 1.883 3.625 3.788 2.82 1.78 .02 1 12 1 3 1 4.036 1.002 5.991

Base64 of yis (использовать base64 -dдля воспроизведения оригинального файла):

H4sIAAAAAAACAwTB226DMAwA0G9LvEJQIbVi70LfHPBoJAiSaR729zsnBB2LVw/x0UWNMm1up4IE
+90ZCC1cvsCm2mkscEJ71l56dRept7ulTDY/Lebp5CW19MLVbbAOlSrlgfVH4fIyCihaXPGg49b6
lfPHXzhvxsecxxZ+Wb6TPq7B8O1a2HjH7Aue7p1qZ0ncgsKvz/8WxuRGoigGgfcfxYvA8r7kn9iA
ei6ohAt/+lzuihmD1PFnMrdIV0PeNfOczh3Ylrw8hnHaM6w1WC8V3X4hcYjOfbKlTyz0pewsP5nh
plOUK9mkPzkd4HLiCbktIGyQI5uaUvZzNBrwLhOf9hJij+Jo5WBf6mHLfh2OFqeaxOHbaGAZl5mL
h5UBI3Hlx99GX4llPumDjgw+NIee7uCaRbrZkzluIhJYi2E0ZU2gb5OnYBTSJQMRfv91irmCz4KK
B5Va5J7T7IGjHnR22PeAd+m3F3KW/voz4BMFPGNgxHE0Loq65M6+Pw==

Сумма md5 есть d392b0f5516033f2ae0985745f299efd.

объяснение

Числа в файле являются приращениями относительной атомной массы (поэтому относительная атомная масса лития равна 1.008 + 0 + 2.995 + 2.937).

Этот скрипт работает, преобразовывая химическую формулу в арифметическое выражение с +и *, заменяя каждый символ его относительной атомной массой, а затем передавая выражение в bc. Если формула содержит недопустимые символы, bcвыдает синтаксическую ошибку и ничего не выводит в STDOUT; в этом случае вывод Invalid formula.

Если STDIN пусто, вывод 0. Вложенные скобки поддерживаются.

zcat y>g 2>a # Unzip the file y and output result to the file g. Send standard error to file a (there will be an error because the last 8 bytes are missing)
s=(`sed 's/[A-Z][a-z]*/& /g;q'<g`)  # Read from g to sed; append a space after any capital letter optionally followed by a lowercase letter; exit at the end of the first line so the atomic masses are not processed in this way; parse result as space-separated array and save to variable $s
p=1.008 # In the upcoming loop, $p is the value obtained in the previous iteration
v=`sed 's/[0-9]\+/*&/g;s/(/+(0/g'` # Read from standard input to sed; prepend a * after any sequence of digits; replace ( with +(0; save to $v
r=(`tail -1 g`) # Get the last line of file g; parse as space-separated array; save to $r
for n in {0..91};{ # Loop once for each number from 0 to 91; set $n to the current number each iteration
p=`bc<<<$p+${r[n]}` # Concatenate $p, + and the next number from $r; send to bc and evaluate as arithmetic expression; save to $p (i.e. add the next increment from the file to $p)
v=${v//${s[n]}/+$p} # Replace every occurence of the current element symbol with + and $p (i.e. replace each symbol with a + and its relative atomic mass
} # end loop
o=`bc<<<0$v 2>a` # Prepend 0 to $v; send to bc and evaluate as arithmetic expression; redirect any errors on standard error to the file a; save result to $o
echo ${o:-Invalid formula} # If $o is empty (if there was a syntax error), output Invalid formula; otherwise output $o

пример

C6H2(NO2)3CH3  #input
C*6H*2+(0NO*2)*3CH*3  #after line 3
+12.011*6+*1.008*2+(0+14.007+15.999*2)*3+12.011+1.008*3  #after the loop in lines 4-6
0+12.011*6+*1.008*2+(0+14.007+15.999*2)*3+12.011+1.008*3  #given to bc in line 7
227.132 #output as evaluated by bc

источник
1
Вы могли бы сэкономить много, сохраняя различия между массами элементов и вычисляя на лету. Минимальные и максимальные различия составляют -1,002 и +6,993 (по обе стороны от Па). При перестановке элементов в порядке атомных масс вместо порядка атомных номеров диапазон становится от 0 до 5.991. Когда была разработана периодическая таблица, было много споров о том, какой порядок лучше. (Очевидно, с точки зрения химии , порядок атомных номеров лучше, но они решили, что это займет некоторое время.)
Level River St
@steveverrill Я думал об этом, когда пишу код, я скоро его
@steveverrill Готово, экономия составила около 33 символов
2

Perl - 924

Он использует серию операций подстановки регулярных выражений для введенной формулы, чтобы развернуть подписанные элементы и группы, заменить элементы атомными весами и преобразовать их в последовательность дополнений, которую он затем оценивает.

$_=<>;chop;@g='H1008He4003Li6940Be9012B10810C12011N14007O15999F18998Ne20179Na22989Mg24305Al26981Si28085P30974S32060Cl35450Ar39948K39098Ca40078Sc44955Ti47867V50942Cr51996Mn54938Fe55845Co58933Ni58693Cu63546Zn65380Ga69723Ge72630As74922Se78960Br79904Kr83798Rb85468Sr87620Y88906Zr91224Nb92906Mo95960Tc98Ru101070Rh102906Pd106420Ag107868Cd112411In114818Sn118710Sb121760Te127600I126904Xe131293Cs132905Ba137327La138905Ce140116Pr140907Nd144242Pm145Sm150360Eu151964Gd157250Tb158925Dy162500Ho164930Er167259Tm168934Yb173054Lu174967Hf178490Ta180948W183840Re186207Os190230Ir192217Pt195084Au196967Hg200592Tl204380Pb207200Bi208980Po209At210Rn222Fr223Ra226Ac227Th232038Pa231036U238028'=~/(\D+)([\d\.]+)/g;for$b(0..91){$d=2*$b+1;$g[$d]=$g[$d]>999?$g[$d]/1000:$g[$d]}%h=@g;for$a('(\(((?>[^()]|(?1))*)\))(\d+)','()([A-Z][a-z]?)(\d+)'){for(;s/$a/$2x$3/e;){}}s/([A-Z][a-z]?)/($h{$1}||_).'+'/ge;if(/_/){print'Invalid formula';exit}$_.=0;print eval;
faubi
источник
2

Mathematica 9 - 247 227

Это явно обманывает, так как я использую функцию, которая напрямую вычисляет физические массы (но не молярные массы!):

r=StringReplace;f[s_]:=Check[ToExpression@r[r[r[s,x:RegularExpression["[A-Z][a-z]*"]:>"ElementData[\""<>x<>"\",\"AtomicWeight\"]+"],x:DigitCharacter..:>"*"<>x<>"+"],{"+*"->"*","+"~~EndOfString->"","+)"->")"}],"Invalid formula"]

Использование: вызов функции fсо строкой, содержащей формулу, на выходе будет масса.

Тестовое задание:

f["H2O"]           (* => 18.0153 *)
f["O"]             (* => 15.9994 *)
f["C6H2(NO2)3CH3"] (* => 227.131 *)
f["FOOF"]          (* => 69.9956 *)
f["C6H12Op3"]      (* => Invalid formula *)
f["Np"]            (* => 237 *)
f["((C)3)4"]       (* => 144.128 *)
f["CodeGolf"]      (* => Invalid formula *)

Mathematica 10 выводит не сырое число, а число с единицей, поэтому это может быть неприемлемо.

Tyilo
источник
@ MartinBüttner Спасибо, я просто скопировал функцию из моего файла инициализации и немного поиграл в нее. Игра в гольф, безусловно, может быть улучшена.
Tyilo
2

Javascript, 1002

Вход в qи выход в a. Я не был уверен в том, каковы правила округления, поэтому я урезал до 3 знаков после запятой (или меньше, если цифры не были доступны из Википедии).

H=1.008
He=4.002
Li=6.94
Be=9.012
B=10.812
C=12.011
N=14.007
O=15.999
F=18.998
Ne=20.179
Na=22.989
Mg=24.305
Al=26.981
Si=28.085
P=30.973
S=32.06
Cl=35.45
Ar=39.948
K=39.098
Ca=40.078
Sc=44.955
Ti=47.867
V=50.941
Cr=51.996
Mn=54.938
Fe=55.845
Co=58.933
Ni=58.693
Cu=63.546
Zn=65.38
Ga=69.723
Ge=72.630
As=74.921
Se=78.96
Br=79.904
Kr=83.798
Rb=85.467
Sr=87.62
Y=88.905
Zr=91.224
Nb=92.906
Mo=95.96
Tc=98
Ru=101.07
Rh=102.905
Pd=106.42
Ag=107.868
Cd=112.411
In=114.818
Sn=118.710
Sb=121.760
Te=127.60
I=126.904
Xe=131.293
Cs=132.905
Ba=137.327
La=138.905
Ce=140.116
Pr=140.907
Nd=144.242
Pm=145
Sm=150.36
Eu=151.964
Gd=157.25
Tb=158.925
Dy=162.500
Ho=164.930
Er=167.259
Tm=168.934
Yb=173.054
Lu=174.966
Hf=178.49
Ta=180.947
W=183.84
Re=186.207
Os=190.23
Ir=192.217
Pt=195.084
Au=196.966
Hg=200.592
Tl=204.38
Pb=207.2
Bi=208.980
Po=209
At=210
Rn=222
Fr=223
Ra=226
Ac=227
Th=232.038
Pa=231.035
U=238.028
try{a=eval(q.replace(/(\d+)/g,'*$1').replace(/(\w)(?=[A-Z\(])/g,'$1+'))}catch(e){a="Invalid formula"}
Zaq
источник
+1 за идею использования простых переменных ... теперь я могу оставить свой слишком сложный ответ для будущего позора
edc65
Ввод на самом деле q, хотя все остальное кажется в порядке. +1
es1024
Вы можете сэкономить несколько байтов, удалив завершающие нули: 121.760 = 121.76
Fels
1

Javascript (E6) 1231

Как функция с вводом в качестве аргумента и возвращением вывода. Точность: 3 десятичных знака

Используйте regexp для преобразования химической формулы в простое арифметическое выражение с суммами и продуктами, заменяя:

  • ( с +(
  • любая числовая последовательность с '*', затем числовая последовательность
  • любая заглавная буква, за которой следуют буквы со знаком «+», затем атомная масса соответствующего элемента (если найден)

Затем выражение оценивается и возвращается значение. В случае ошибок или если значение равно NaN (или ноль), функция возвращает «Неверная формула»

Теперь я вижу, что все остальные ответы используют один и тот же метод ... ну, вот версия javascript

F=f=>{
  T={H:1.008,He:4.002,Li:6.94,Be:9.012,B:10.81,C:12.011
  ,N:14.007,O:15.999,F:18.998,Ne:20.179,Na:22.989,Mg:24.305
  ,Al:26.981,Si:28.085,P:30.973,S:32.06,Cl:35.45,Ar:39.948
  ,K:39.098,Ca:40.078,Sc:44.955,Ti:47.867,V:50.941,Cr:51.996,Mn:54.938
  ,Fe:55.845,Co:58.933,Ni:58.693,Cu:63.546,Zn:65.38,Ga:69.723,Ge:72.630
  ,As:74.921,Se:78.96,Br:79.904,Kr:83.798,Rb:85.467,Sr:87.62,Y:88.905,Zr:91.224
  ,Nb:92.906,Mo:95.96,Tc:98,Ru:101.07,Rh:102.905,Pd:106.42,Ag:107.868,Cd:112.411
  ,In:114.818,Sn:118.710,Sb:121.760,Te:127.60,I:126.904,Xe:131.293
  ,Cs:132.905,Ba:137.327,La:138.905,Ce:140.116,Pr:140.907,Nd:144.242,Pm:145
  ,Sm:150.36,Eu:151.964,Gd:157.25,Tb:158.925,Dy:162.500,Ho:164.930,Er:167.259
  ,Tm:168.934,Yb:173.054,Lu:174.966,Hf:178.49,Ta:180.947,W:183.84,Re:186.207
  ,Os:190.23,Ir:192.217,Pt:195.084,Au:196.966,Hg:200.592,Tl:204.38,Pb:207.2
  ,Bi:208.980,Po:209,At:210,Rn:222,Fr:223,Ra:226,Ac:227,Th:232.038,Pa:231.035
  ,U:238.028,Np:237,Pu:244,Am:243,Cm:247,Bk:247,Cf:251,Es:252,Fm:257,Md:258
  ,No:259,Lr:266,Rf:267,Db:268,Sg:269,Bh:270,Hs:269,Mt:278
  ,Ds:281,Rg:281,Cn:285,Uut:286,Fl:289,Uup:289,Lv:293,Uus:294,Uuo:294};
  e='Invalid formula';
  try{return eval(f.replace(/([A-Z][a-z]*)|(\d+)|(\()/g,(f,a,b,c)=>c?'+(':b?'*'+b:a='+'+T[a]))||e}
  catch(x){}return e
}
edc65
источник
Я использовал почти тот же метод, но я создал эту функцию, по крайней мере, год назад, поэтому, по крайней мере, я вас не скопировал. ;)
Tyilo
1

PHP - 793 (583 + 210)

В значительной степени опередил ответ Professorfish , который использует аналогичный метод, но эй ... Символы и массы сжаты gzip в файле, aполученном с помощью следующего кода:

$symbolsList = ['H', 'He', 'Li', 'Be', 'B', 'C', 'N', 'O', 'F', 'Ne', 'Na', 'Mg', 'Al', 'Si', 'P', 'S', 'Cl', 'Ar', 'K', 'Ca', 'Sc', 'Ti', 'V', 'Cr', 'Mg', 'Fe', 'Co', 'Ni', 'Cu', 'Zn', 'Ga', 'Ge', 'As', 'Se', 'Br', 'Kr', 'Rb', 'Sr', 'Y', 'Zr', 'Nb', 'Mo', 'Tc', 'Ru', 'Rh', 'Pd', 'Ag', 'Cd', 'In', 'Sn', 'Sb', 'Te', 'I', 'Xe', 'Cs', 'Ba', 'La', 'Ce', 'Pr', 'Nd', 'Pm', 'Sm', 'Eu', 'Gd', 'Tb', 'Dy', 'Ho', 'Er', 'Tm', 'Yb', 'Lu', 'Hf', 'Ta', 'W', 'Re', 'Os', 'Ir', 'Pt', 'Au', 'Hg', 'Tl', 'Pb', 'Bi', 'Po', 'At', 'Rn', 'Fr', 'Ra', 'Ac', 'Th', 'Pa', 'U'];
$massesList = [1.008, 4.003, 6.94, 9.012, 10.81, 12.011, 14.007, 15.999, 18.998, 20.18, 22.99, 24.305, 26.982, 28.085, 30.974, 32.06, 35.45, 39.948, 39.098, 40.078, 44.956, 47.867, 50.942, 51.996, 54.938, 55.845, 58.933, 58.6934, 63.546, 65.38, 69.726, 72.630, 74.922, 78.96, 79.904, 83.798, 85.468, 87.62, 88.906, 91.224, 92.906, 95.96, 98, 101.07, 102.906, 106.42, 107.868, 112.411, 114.818, 118.710, 121.760, 127.60, 126.904, 131.293, 132.905, 137.327, 138.905, 140.116, 140.908, 144.242, 145, 150.36, 151.964, 157.25, 158.925, 162.5, 164.93, 167.259, 168.934, 173.054, 174.967, 178.49, 180.948, 183.84, 186.207, 190.23, 192.217, 195.084, 196.967, 200.592, 204.38, 207.2, 208.98, 209, 210, 222, 223, 226, 227, 232.038, 231.036, 238.029];

$fileArrayContent = [$symbolsList, $massesList];
$fileStringContent = json_encode($fileArrayContent);

$file = gzopen('a', 'w9');
gzwrite($file, $fileStringContent);
gzclose($file);

Формула должна храниться в $fпеременной:

$a=json_decode(gzfile('a')[0]);$r=@eval('return '.str_replace($a[0],$a[1],preg_replace(['#(?<=(?!\().)(\(|'.implode('|',$a[0]).')#','#\d+#'],['+${1}','*$0'],$f)).';');echo error_get_last()?'Invalid formula':$r;

Вот версия без комментариев и комментариев:

// Recover the data stored in the compressed file
$fileStringContent = gzfile('a')[0];
$fileArrayContent = json_decode($fileStringContent);
$symbolsList = $fileArrayContent[0];
$massesList = $fileArrayContent[1];

$formula = preg_replace('#(?<=(?!\().)(\(|'. implode('|', $symbolsList) .')#', '+${1}', $formula); // Add a "+" before each opening paranthesis and symbol not at the beginning of the string and not preceded by an opening paranthesis
$formula = preg_replace('#\d+#', '*$0', $formula); // Add a "*" before each number

$formula = str_replace($symbolsList, $massesList, $formula); // Replace each symbol with its corresponding mass

$result = @eval('return '. $formula .';'); // Evaluate the value without showing the errors
echo error_get_last() ? 'Invalid formula' : $result; // Print the value, or "Invalid formula" if there was an error
Черная дыра
источник
Сейчас я
@professfishfish Действительно! Кроме того, я не учел размер файла, который больше того, что я говорил ранее. Вы, безусловно, победитель, в настоящее время ^^!
Blackhole
1

Скала, 1077

Я вижу все ваши решения на языках с динамической типизацией с подобными отступами evalили встроенной функцией атомной массы и предлагаю вам статически типизированное языковое решение:

object M{type S=String;type Z=(Int,S)
val d="H  *dHe JVLi inBe!!rB !5\"C !AiN !W!O !l3F \".*Ne\":_Na\"XUMg\"fUAl#%#Si#0iP #OOS #[&Cl$!,Ar$P|K $GxCa$RBSc%(7Ti%G5V %gwCr%s.Mn&4JFe&>)Co&^yNi&\\ECu'2\"Zn'ERGa'seGe(4^As(M#Se(x Br)$$Kr)MLRb)_5Sr)v,Y *%kZr*>LNb*PBMo*ppTc+(TRu+I4Rh+\\ePd,$,Ag,3RCd,cqIn,}LSn-HrSb-i>Te.IJI .B$Xe.peCs/#sBa/RwLa/ccCe/pXPr/y!Nd0>NPm0FTSm1!VEu12\\Gd1jrTb1|aDy2DdHo2^VEr2wATm3+0Yb3W Lu3k@Hf42nTa4L{W 4kfRe5&wOs5QdIr5fqPt6'BAu6;DHg6azTl7,8Pb7J8Bi7]2Po7]FAt7h$Rn9+bFr96@Ra9V8Ac9`tTh:8NPa:-mU :x4".grouped(5).map{s=>(s.take(2).trim,s(2)*8836+s(3)*94+s(4)-285792)}.toMap
def k(n:S):Z={val x=n.takeWhile(_.isDigit);(if(x=="")1 else x.toInt,n drop x.length)}
def o(m:S):Z={if(m(0)==40){val(i,s)=a(m.tail);if(s(0)!=41)???;val(j,t)=k(s.tail);(j*i,t)}else{val l=if(m.size>1&&m(1).isLower)2 else 1;val(i,s)=d(m.take(l))->m.drop(l);val(j,t)=k(s);(j*i,t)}}
def a(m:S)={var(r,s)=(m,0);do{val(y,z)=o(r);r=z;s+=y}while(r!=""&&r(0)!=41);s->r}
def main(q:Array[S]){println(try{val(m,s)=a(io.Source.stdin.getLines.next);if(s!="")???;m/1e3}catch{case _=>"Invalid formula"})}}

Я должен рассмотреть некоторое сжатие для данных, но сейчас давайте просто сохраним атомные массы в базе 94.

Karol S
источник
Дает мнеmolarmass.scala:5: error: ';' expected but identifier found. def a(m:S)={var(r,s)=(m,0);do{val(y,z)=o(r);r=z;s+=y}while(r!=""&&r(0)!=41)s->r}
es1024
Исправлена. Также я узнал, что парсер Скала странный.
Karol S