Znajdź pana danego związku!

12

Wyzwanie

Biorąc pod uwagę wzór substancji chemicznej, uzyskaj Mr związku.

Równanie

Po każdym pierwiastku w związku występuje liczba oznaczająca liczbę wspomnianego atomu w związku. Jeśli nie ma liczby, w związku jest tylko jeden z tych atomów.

Oto niektóre przykłady:

  • Etanol (C 2 H 6 O) byłby C2H6Otam, gdzie są dwa atomy węgla, 6 atomów wodoru i 1 atom tlenu
  • Wodorotlenek magnezu (MgO, 2 H 2 ) byłoby MgO2H2w którym znajduje się jeden atom magnezu, dwa atomy tlenu i dwa atomy wodoru.

Pamiętaj, że nigdy nie będziesz musiał obsługiwać nawiasów, a każdy element jest uwzględniany tylko raz w formule.

Podczas gdy większość ludzi prawdopodobnie będzie trzymać się kolejności, w której czują się najlepiej, nie ma ścisłego systemu zamówień. Na przykład, woda może być podawany albo jako H2Olub OH2.

M r

Uwaga: Załóżmy, że masa wzoru jest taka sama jak masa cząsteczkowa

M R związku, masy cząsteczkowej, to suma masy atomowej atomów w cząsteczce.

Jedyne pierwiastki i ich masy atomowe do 1 miejsca po przecinku, które musisz wesprzeć (wodór do wapnia, nie licząc gazów szlachetnych) są następujące. Można je również znaleźć tutaj

H  - 1.0      Li - 6.9      Be - 9.0
B  - 10.8     C  - 12.0     N  - 14.0
O  - 16.0     F  - 19.0     Na - 23.0
Mg - 24.3     Al - 27.0     Si - 28.1
P  - 31.0     S  - 32.1     Cl - 35.5
K  - 39.1     Ca - 40.1

Zawsze należy podawać wynik z dokładnością do jednego miejsca po przecinku.

Na przykład, etanol ( C2H6O) ma M r z 46.0, jak to jest suma masy atomowej elementy w go:

12.0 + 12.0 + 1.0 + 1.0 + 1.0 + 1.0 + 1.0 + 1.0 + 16.0
(2*C + 6*H + 1*O)

Wejście

Pojedynczy ciąg w powyższym formacie. Możesz zagwarantować, że elementy zawarte w równaniu będą rzeczywistymi symbolami żywiołów.

Nie ma gwarancji, że dany związek istnieje w rzeczywistości.

Wynik

Całkowity Mr związku, do 1 miejsca po przecinku.

Zasady

Wbudowane, które mają dostęp do danych pierwiastków lub danych chemicznych są niedozwolone (przepraszam Mathematica)

Przykłady

Input > Output
CaCO3 > 100.1
H2SO4 > 98.1
SF6 > 146.1
C100H202O53 > 2250.0

Zwycięski

Najkrótszy kod w bajtach wygrywa.

Ten post został przyjęty za zgodą Cairney Coheringaahing . (Opublikuj teraz usunięte)

Rozpad beta
źródło
Czy musimy obsługiwać kwantyfikatory, takie jak 2H2O:?
Pan Xcoder,
6
Dla ciekawskich jest to rozwiązanie Mathematica (53 bajty):NumberForm[#&@@#~ChemicalData~"MolecularMass",{9,1}]&
JungHwan Min

Odpowiedzi:

6

Galaretka , 63 bajty

ḟØDOP%⁽¡ṛị“ÇṚÆ’BH+“Ḳ"ɦṀ⁷6<s¡_-¦y⁼Ḟ¡¡FPɓ‘¤÷5
fØDVȯ1×Ç
Œs>œṗ⁸ḊÇ€S

Monadyczny link przyjmujący listę znaków i zwracający liczbę.

Wypróbuj online!

W jaki sposób?

ḟØDOP%⁽¡ṛị“ÇṚÆ’BH+“Ḳ"ɦṀ⁷6<s¡_-¦y⁼Ḟ¡¡FPɓ‘¤÷5 - Link 1, Atomic weight: list of characters
                                            -                              e.g. "Cl23"
 ØD                                         - digit yield = "0123456789"
ḟ                                           - filter discard                      "Cl"
   O                                        - cast to ordinals                [67,108]
    P                                       - product                            7236
      ⁽¡ṛ                                   - base 250 literal = 1223
     %                                      - modulo                             1121
                                        ¤   - nilad followed by link(s) as a nilad:
          “ÇṚÆ’                             -   base 250 literal  = 983264
               B                            -   convert to binary = [    1,    1,     1,     1,   0,  0,  0,   0, 0,  0,  0, 0,     1,     1,     1, 0, 0,  0,  0,   0]
                H                           -   halve             = [  0.5,  0.5,   0.5,   0.5,   0,  0,  0,   0, 0,  0,  0, 0,   0.5,   0.5,   0.5, 0, 0,  0,  0,   0]
                  “Ḳ"ɦṀ⁷6<s¡_-¦y⁼Ḟ¡¡FPɓ‘    -   code-page indexes = [177  , 34  , 160  , 200  , 135, 54, 60, 115, 0, 95, 45, 5, 121  , 140  , 195  , 0, 0, 70, 80, 155]
                 +                          -   addition          = [177.5, 34.5, 160.5, 200.5, 135, 54, 60, 115, 0, 95, 45, 5, 121.5, 140.5, 195.5, 0, 0, 70, 80, 155]
         ị                                  - index into (1-indexed and modular)
                                            -    ...20 items so e.g. 1121%20=1 so 177.5
                                         ÷5 - divide by 5                          35.5

fØDVȯ1×Ç - Link 2: Total weight of multiple of atoms: list of characters   e.g. "Cl23"
 ØD      - digit yield = "0123456789"
f        - filter keep                                                            "23"
   V     - evaluate as Jelly code                                                  23
    ȯ1   - logical or with one (no digits yields an empty string which evaluates to zero)
       Ç - call last link (1) as a monad (get the atomic weight)                   35.5
      ×  - multiply                                                               816.5

Œs>œṗ⁸ḊÇ€S - Main link: list of characters                             e.g. "C24HCl23"
Œs         - swap case                                                      "c24hcL23"
  >        - greater than? (vectorises)                                      10011000
     ⁸     - chain's left argument                                          "C24HCl23"
   œṗ      - partition at truthy indexes                          ["","C24","H","Cl23"]
      Ḋ    - dequeue                                                 ["C24","H","Cl23"]
       Ç€  - call last link (2) as a monad for €ach                  [  288,  1,  816.5]
         S - sum                                                                 1105.5
Jonathan Allan
źródło
To jedna z najdłuższych odpowiedzi na galaretki, jakie kiedykolwiek widziałem, ale wciąż jest ona mniejsza niż połowa długości programu na drugim miejscu, więc dobra robota!
Gryphon
6

Python 3 ,  189 182  168 bajtów

-14 bajtów przy użyciu skrótu z odpowiedzi JavaScript (ES6) Justina Marinera .

import re
lambda s:sum([[9,35.5,39.1,24.3,28.1,14,16,31,40.1,23,32.1,10.8,12,27,6.9,19,0,1][int(a,29)%633%35%18]*int(n or 1)for a,n in re.findall("(\D[a-z]?)(\d*)",s)])

Wypróbuj online!


Poniżej znajduje się wersja 182-bajtowa, wyjaśnię to - powyższe po prostu zmienia kolejność wag, używa intdo konwersji nazwy elementu z bazy 29i używa różnych dywidend w celu zmniejszenia zakresu liczb całkowitych - patrz Justin Odpowiedź Marinera .

import re
lambda s:sum([[16,31,40.1,32.1,0,24.3,12,39.1,28.1,19,0,9,10.8,23,27,35.5,6.9,14,1][ord(a[0])*ord(a[-1])%1135%98%19]*int(n or 1)for a,n in re.findall("(\D[a-z]?)(\d*)",s)])

Nienazwana funkcja akceptująca ciąg znaków si zwracająca liczbę.

Wypróbuj online!

W jaki sposób?

Używa wyrażenia regularnego, aby podzielić dane wejściowe, sna elementy i ich liczbę, używając:
re.findall("(\D[a-z]?)(\d*)",s)
\Ddopasowuje dokładnie jedną cyfrę i [a-z]?dopasowuje 0 lub 1 małą literę, razem dopasowując elementy. \d*dopasowuje 0 lub więcej cyfr. Nawiasy, aby te dwie grupy i jako taki findall("...",s)zwraca listę krotek ciągi [(element, number),...].

Liczba jest łatwa do wyodrębnienia, jedyną rzeczą do zrobienia jest to, że pusty ciąg oznacza 1, osiąga się to logicznie or , ponieważ Python ciągi są falsey: int(n or 1).

Ciąg elementu otrzymuje unikalny numer, biorąc iloczyn rzędów pierwszego i ostatniego znaku (zwykle są one takie same, np. S lub C, ale musimy rozróżnić Cl, C, Ca i Na, więc nie możemy po prostu użyć jednego postać).

Liczby te są następnie mieszane, aby objąć znacznie mniejszy zakres [0,18], znaleziony w wyniku przeszukania przestrzeni modułu %1135%98%19. Na przykład "Cl"ma liczby porządkowe 67i 108, które mnożą się, aby dać 7736, który 1135to modulo jest 426, który 98jest modulo 34, który 19jest modulo 15; liczba ta służy do indeksowania do listy liczb całkowitych - 15. wartość (indeksowana 0) na liście:
[16,31,40.1,32.1,0,24.3,12,39.1,28.1,19,0,9,10.8,23,27,35.5,6.9,14,1]
jest35.5 to masa atomowa Cl, która jest następnie mnożona przez liczbę takich pierwiastków (jak stwierdzono powyżej).

Produkty te są następnie dodawane razem za pomocą sum(...).

Jonathan Allan
źródło
Jesteś geniuszem ... Obezwładnił mnie ponad 350 bajtami
Mr. Xcoder
4

PHP , 235 bajtów

preg_match_all("#([A-Z][a-z]?)(\d*)#",$argn,$m);foreach($m[1]as$v)$s+=array_combine([H,Li,Be,B,C,N,O,F,Na,Mg,Al,Si,P,S,Cl,K,Ca],[1,6.9,9,10.8,12,14,16,19,23,24.3,27,28.1,31,32.1,35.5,39.1,40.1])[$v]*($m[2][+$k++]?:1);printf("%.1f",$s);

Wypróbuj online!

Zamiast tego array_combine([H,Li,Be,B,C,N,O,F,Na,Mg,Al,Si,P,S,Cl,K,Ca],[1,6.9,9,10.8,12,14,16,19,23,24.3,27,28.1,31,32.1,35.5,39.1,40.1])możesz używać [H=>1,Li=>6.9,Be=>9,B=>10.8,C=>12,N=>14,O=>16,F=>19,Na=>23,Mg=>24.3,Al=>27,Si=>28.1,P=>31,S=>32.1,Cl=>35.5,K=>39.1,Ca=>40.1]tej samej liczby bajtów

Jörg Hülsermann
źródło
3

JavaScript (ES6), 150 bajtów

c=>c.replace(/(\D[a-z]?)(\d+)?/g,(_,e,n=1)=>s+=[9,35.5,39.1,24.3,28.1,14,16,31,40.1,23,32.1,10.8,12,27,6.9,19,0,1][parseInt(e,29)%633%35%18]*n,s=0)&&s

Zainspirowany odpowiedzią Jonathana Allana na Python , w której wyjaśnił, jak nadać każdemu elementowi unikalny numer i mieszać te liczby w mniejszym zakresie.

Elementy zostały przekształcone w unikalne liczby, interpretując je jako base-29 (0-9 i AS). Potem odkryłem, że %633%35%18zawęża wartości do zakresu [0, 17]przy zachowaniu wyjątkowości.

Test Snippet

f=
c=>c.replace(/(\D[a-z]?)(\d+)?/g,(_,e,n=1)=>s+=[9,35.5,39.1,24.3,28.1,14,16,31,40.1,23,32.1,10.8,12,27,6.9,19,0,1][parseInt(e,29)%633%35%18]*n,s=0)&&s
Input: <input oninput="O.value=f(this.value)"><br>
Result: <input id="O" disabled>

Justin Mariner
źródło
Och, myślę, że twój sposób zaoszczędziłby mi też kilka bajtów!
Jonathan Allan
2

Clojure, 198 194 bajtów

Aktualizacja: lepiej forniż reduce.

#(apply +(for[[_ e n](re-seq #"([A-Z][a-z]?)([0-9]*)"%)](*(if(=""n)1(Integer. n))({"H"1"B"10.8"O"16"Mg"24.3"P"31"K"39.1"Li"6.9"C"12"F"19"Al"2"S"32.1"Ca"40.1"Be"9"N"14"Na"23"Si"28.1"Cl"35.5}e))))

Oryginał:

#(reduce(fn[r[_ e n]](+(*(if(=""n)1(Integer. n))({"H"1"B"10.8"O"16"Mg"24.3"P"31"K"39.1"Li"6.9"C"12"F"19"Al"2"S"32.1"Ca"40.1"Be"9"N"14"Na"23"Si"28.1"Cl"35.5}e))r))0(re-seq #"([A-Z][a-z]?)([0-9]*)"%))

Zastanawiam się, czy istnieje bardziej zwarty sposób kodowania tabeli przeglądowej.

NikoNyrh
źródło
2

Python 3 , 253 bajty

def m(f,j=0):
 b=j+1
 while'`'<f[b:]<'{':b+=1
 c=b
 while'.'<f[c:]<':':c+=1
 return[6.9,9,23,40.1,24.3,27,28.1,35.5,31,32.1,39.1,1,10.8,12,14,16,19]['Li Be Na Ca Mg Al Si Cl P S K H B C N O F'.split().index(f[j:b])]*int(f[b:c]or 1)+(f[c:]>' 'and m(f,c))

Wypróbuj online!

ovs
źródło
1

Mathematica, 390 338 329 bajtów

Zaoszczędziłem 9 bajtów, ponieważ nie śpię i korzystam z zamierzonego skrótu.

Wersja 2.1:

S=StringSplit;Total[Flatten@{ToExpression@S[#,LetterCharacter],S[#,DigitCharacter]}&/@S[StringInsert[#,".",First/@StringPosition[#,x_/;UpperCaseQ[x]]],"."]/.{"H"->1,"Li"->3,"Be"->9,"B"->10.8,"C"->12,"N"->14,"O"->16,"F"->19,"Na"->23,"Mg"->24.3,"Al"->27,"Si"->28.1,"P"->31,"S"->32.1,"Cl"->35.5,"K"->39.1,"Ca"->40.1}/.{a_,b_}->a*b]&

Objaśnienie: Znajdź pozycję wszystkich wielkich liter. Umieść kropkę przed każdym. Podziel ciąg w każdej kropce. Dla tej listy podciągów wykonaj następujący podział na podstawie liter i podziel na podstawie cyfr. Dla podzielonych na litery przekształć ciąg znaków na liczby. Dla tych podzielonych cyframi zamień każdą substancję chemiczną na jej masę cząsteczkową. Dla każdego o masie cząsteczkowej i liczbie atomów zamień go na ich iloczyn. Oni znajdują sumę.

Wersja 1:

Jestem pewien, że można to dużo zagrać w golfa (lub po prostu całkowicie przepisać). Chciałem tylko wymyślić, jak to zrobić. (Zastanowi się nad tym rano).

F=Flatten;d=DigitCharacter;S=StringSplit;Total@Apply[Times,#,2]&@(Transpose[{F@S[#,d],ToExpression@F@S[#,LetterCharacter]}]&@(#<>If[StringEndsQ[#,d],"","1"]&/@Fold[If[UpperCaseQ[#2],Append[#,#2],F@{Drop[#,-1],Last[#]<>#2}]&,{},StringPartition[#,1]]))/.{"H"->1,"Li"->3,"Be"->9,"B"->10.8,"C"->12,"N"->14,"O"->16,"F"->19,"Na"->23,"Mg"->24.3,"Al"->27,"Si"->28.1,"P"->31,"S"->32.1,"Cl"->35.5,"K"->39.1,"Ca"->40.1}&

Objaśnienie: Najpierw podziel łańcuch na znaki. Następnie złóż tablicę łączącą małe litery i cyfry z powrotem do ich stolicy. Następnie dodaj 1 do dowolnej substancji chemicznej bez numeru na końcu. Następnie wykonaj dwa podziały terminów w tablicy - jeden podział na wszystkie liczby i jeden podział na wszystkie litery. Najpierw zamień litery na ich masy molowe, a następnie znajdź iloczyn skalarny tych dwóch list.

Ian Miller
źródło
1

Python 3 - 408 bajtów

Jest to głównie rozwiązanie @ovs, ponieważ grał w golfa o ponad 120 bajtów ... Zobacz wstępne rozwiązanie poniżej.

e='Li Be Na Ca Mg Al Si Cl P S K H B C N O F'.split()
f,g=input(),[]
x=r=0
for i in e:
 if i in f:g+=[(r,eval('6.9 9 23 40.1 24.3 27 28.1 35.5 31 32.1 39.1 1 10.8 12 14 16 19'.split()[e.index(i)]))];f=f.replace(i,' %d- '%r);r+=1
h=f.split()
for c,d in zip(h,h[1:]):
 s=c.find('-')
 if-1<s:
  if'-'in d:
   for y in g:x+=y[1]*(str(y[0])==c[:s])
  else:
   for y in g:x+=y[1]*int(d)*(str(y[0])==c[:s])
print(x)

Wypróbuj online!

Python 3 - 550 548 535 bajtów (stracił licznik z wcięciem)

Zaoszczędzono 10 bajtów dzięki @cairdcoinheringaahing i 3 zaoszczędzono dzięki ovs

Miałem osobisty cel, aby nie używać żadnego wyrażenia regularnego i robić to w zabawny, oldschoolowy sposób ... Okazało się, że jest o 350 bajtów dłuższy niż rozwiązanie wyrażenia regularnego, ale korzysta tylko ze standardowej biblioteki Pythona ...

a='Li6.9 Be9. Na23. Ca40.1 Mg24.3 Al27. Si28.1 Cl35.5 P-31. S-32.1 K-39.1 H-1. B-10.8 C-12. N-14. O-16. F-19.'.split()
e,m,f,g,r=[x[:1+(x[1]>'-')]for x in a],[x[2:]for x in a],input(),[],0
for i in e:
 if i in f:g.append((r,float(m[e.index(i)])));f=f.replace(i,' '+str(r)+'- ');r+=1;
h,x=f.split(),0
for i in range(len(h)):
 if '-'in h[i]:
    if '-'in h[i+1]:
     for y in g:x+=y[1]*(str(y[0])==h[i][:h[i].index('-')])
    else:
        for y in g:
         if str(y[0])==h[i][:h[i].index('-')]:x+=(y[1])*int(h[i+1])
 else:1
print(x)  

Wypróbuj online!


Jeśli ktoś chce zagrać w golfa (z poprawkami wcięć i innymi sztuczkami ...), zostanie w 100% dobrze przyjęty, czując, że jest lepszy sposób na zrobienie tego ...

Pan Xcoder
źródło
Można wymienić for y in g: if str(y[0])==h[i][:h[i].index('-')]:x+=y[1]zfor y in g:x+=y[1]*(str(y[0])==h[i][:h[i].index('-')])
Caird coinheringaahing
@cairdcoinheringaahing ah, świetnie ... aktualizacja, gdy mam dostęp do komputera
Mr. Xcoder
@ovs Wielkie dzięki! Podziękowania w odpowiedzi
Pan Xcoder,
W Pythonie można użyć średnika zamiast znaku nowej linii, co pozwala zapisywać bajty przy wcięciu.
Pavel
@ Feniks nie, jeśli jest if/for/whilew następnym wierszu. Tak jest w przypadku każdej wciętej linii, dlatego nie można zapisać bajtów.
ovs