ooxx7788 发表于 2017-6-7 19:50:21

Python: 每日一题 61

本帖最后由 ooxx7788 于 2017-6-8 15:54 编辑

化学是我大学的主要基础专业课,刚好看见了个跟化学有一点点关系,翻译又不需要太长的题目,赶快搬过来。

你会给到一个字符串形式的分子式,请你数出其中的各元素的原子数。具体要求看例子:

water = 'H2O'
parse_molecule(water)               # return {H: 2, O: 1}

magnesium_hydroxide = 'Mg(OH)2'
parse_molecule(magnesium_hydroxide)   # return {Mg: 1, O: 2, H: 2}

fremy_salt = 'K42'
parse_molecule(fremySalt)             # return {K: 4, O: 14, N: 2, S: 4}

我想原子数怎么数应该不用我多解释吧,注意计算顺序哦,一不小心就算错了。还有怎么区分元素呢?哈哈,慢慢想吧!我看看有没有人会把元素周期表给抄上来。


**** Hidden Message *****

Messj 发表于 2017-6-7 21:40:46

http://g.hiphotos.baidu.com/zhidao/pic/item/5d6034a85edf8db1ed5b3ea10823dd54574e74f9.jpg

Messj 发表于 2017-6-7 21:41:22

我叫雷锋,不用谢{:10_254:}{:10_256:}

ooxx7788 发表于 2017-6-7 21:47:47

Messj 发表于 2017-6-7 21:40


这是你的百度!

SixPy 发表于 2017-6-7 23:48:07

本帖最后由 SixPy 于 2017-6-7 23:50 编辑

import re
from collections import Counter

def parse_molecule(exp):
        ptns=]+)\](\d+)'),
            re.compile(r'\(([^()]+)\)(\d+)'),
            re.compile(r'(?)(\d+)')]
        repl=lambda m: m.group(1) * int(m.group(2))
        for ptn in ptns:
                while ptn.search(exp):
                        exp=ptn.sub(repl,exp)
        exp=re.sub(r'(?)',r'\1 ',exp).split()
        return dict(Counter(exp))

exp=('H2O','Mg(OH)2','K42')
for e in exp:
        print('%-15s%s'%(e,parse_molecule(e)))

       
H2O            {'O': 1, 'H': 2}
Mg(OH)2      {'O': 2, 'H': 2, 'Mg': 1}
K42{'O': 14, 'N': 2, 'K': 4, 'S': 4}

ooxx7788 发表于 2017-6-8 16:08:59

SixPy 发表于 2017-6-7 23:48


大神给的答案基本正确,只是在以下几个化学式有点问题。第一个化学式有点特殊,光有括号。后面两个大括弧的化学式我也没见过。
Should parse cyclopentadienyliron dicarbonyl dimer: (C5H5)Fe(CO)2CH3
Should parse really weird molecule: As2{Be4C52}4Cu5
Should parse hexol sulphate: {3Co}(SO4)3

SixPy 发表于 2017-6-8 17:02:29

ooxx7788 发表于 2017-6-8 16:08
大神给的答案基本正确,只是在以下几个化学式有点问题。第一个化学式有点特殊,光有括号。后面两个大括 ...

import re
from collections import Counter

def parse_molecule(exp):
        ptns=+)\}(\d+)'),
            re.compile(r'\[([^\[\]]+)\](\d+)'),
            re.compile(r'\(([^()]+)\)(\d+)'),
            re.compile(r'(?)(\d+)')]
        repl=lambda m: m.group(1) * int(m.group(2))
        for ptn in ptns:
                while ptn.search(exp):
                        exp=ptn.sub(repl,exp)
        exp=re.sub(r'[{}\[\]()]','',exp)
        exp=re.sub(r'(?)',r'\1 ',exp).split()
        return dict(Counter(exp))

exp=('H2O','Mg(OH)2','K42','(C5H5)Fe(CO)2CH3',
   'As2{Be4C52}4Cu5','{3Co}(SO4)3')
for e in exp:
        print('%s\n%s\n'%(e,parse_molecule(e)))

H2O
{'H': 2, 'O': 1}

Mg(OH)2
{'H': 2, 'Mg': 1, 'O': 2}

K42
{'S': 4, 'O': 14, 'K': 4, 'N': 2}

(C5H5)Fe(CO)2CH3
{'H': 8, 'C': 8, 'Fe': 1, 'O': 2}

As2{Be4C52}4Cu5
{'As': 2, 'C': 44, 'Co': 24, 'Be': 16, 'B': 8, 'Cu': 5, 'O': 48}

{3Co}(SO4)3
{'H': 42, 'Co': 4, 'N': 12, 'S': 3, 'O': 18}

玄天宗 发表于 2017-6-8 17:30:35

学习

瓦蓝 发表于 2017-6-9 07:59:36

看看

gopythoner 发表于 2017-6-13 16:38:38

反正写了一个函数,你上面这几个是没问题,不知道能不能通用
import re

def parse_molecule(f):
lis = re.findall("*",f)
nums = re.findall("\d+",f)
for i in set(lis):
    f = f.replace(i,"'"+i+"'")
if "(" in f:
    for j in set(nums):
      f = f.replace(j,"*"+str(j))
else:
    for j in set(nums):
      f = f.replace(j,"*"+str(j)+"+")
f = f.replace("**","*").replace("(","+(").replace("[","+(").replace("]",")").replace("''","'+'")
k = eval(f)
dic = {i:k.count(i) for i in set(lis)}
print(dic)
return dic

输出
water = 'H2O'
magnesium_hydroxide = 'Mg(OH)2'
fremy_salt = 'K42'
parse_molecule(water)
parse_molecule(magnesium_hydroxide)
parse_molecule(fremy_salt)

{'H': 2, 'O': 1}
{'O': 2, 'H': 2, 'Mg': 1}
{'O': 14, 'N': 2, 'S': 4, 'K': 4}

gopythoner 发表于 2017-6-16 17:46:41

gopythoner 发表于 2017-6-13 16:38
反正写了一个函数,你上面这几个是没问题,不知道能不能通用




自答,你后面发的那几个复杂的化学式我这个函数解不出来
看来还是不通用啊
这题有点难
其实我老是想着怎么能用eval()方法来解,但是不知道怎么构造出来可以用这个方法的

solomonxian 发表于 2017-7-24 18:55:16

本帖最后由 solomonxian 于 2017-7-24 18:56 编辑

不知道很长的分子式是什么样子的,有没有另外的符号,假设就是例子中的那样输入吧

分四步:
1、处理元素:大写字母单独一个,小写字母跟前一个大写字母搭配
2、递归拆除括号:括号内数据乘以括号后数字,扩展
3、处理数字:用数字与前字符串乘机扩展列表
4、返回统计信息
def parse_molecule(s):
    """parse_molecule(str) -> dict, counts the atoms of each element"""
    list_s = list(s)
    for i in range(len(s)): # 处理元素
      if s.islower():
            list_s = list_s+s
            list_s = ""
    #print(list_s)
    def flatten_lst(lst,m=0): # 拆除括号
      for j in range(len(lst)):
            if lst in {'[','(','{'}:
                if m==0: start = j # 记录左括号初始位置
                m +=1
            elif lst in {']',')','}'}:
                m -=1
            else:
                continue

            if m==0: # 等于0表明括号配对完成
                if lst.isdigit():
                  new = lst*(int(lst)-1) # 括号内容数乘后面数字
                  lst.pop(j+1)# 从后往前删除数字、右括号、左括号
                else:
                  new = [] # 括号右不是数字时为空
                lst.pop(j)
                lst.pop(start)
                lst.extend(new) # 更新列表
                return flatten_lst(lst) # 递归拆括号,直到成功完成for循环
      else:
         # print(lst)
            for k in range(len(lst)): # 处理数字
                if lst.isdigit():
                  lst.extend(lst*(int(lst)-1))
            return {i:lst.count(i) for i in set(lst) if i.isalpha()}
    return flatten_lst(list_s)
试下用re模块做,感觉跟作弊一样方便

import re

def parse_molecule2(s):
    p = re.compile(r"""[\[\(\{]    # 以左括号开头
                  [^\[\]\(\)\{\}]+   # 中间不包含任何括号
                  [\]\)\}]\d*""",re.VERBOSE)# 以右括号加可能数字结尾
    while 1:
      try:
            sub_s = p.search(s).group()
            
            numbers = ['0']
            for j in sub_s[::-1]: # 这段用来找括号外的数字(2~∞),没有就当是1
                if j.isdigit():
                  numbers.append(j)
                else:
                  break
            number = int("".join(numbers)) if len(numbers)>1 else 1
            s = p.sub(sub_s*number,s,1)
      except AttributeError:
            break# 找不到括号就退出
      
    lst = list(s)
    for i in range(len(s)):
      if s.islower():
            lst, lst = "",lst+lst
      if s.isdigit():
            lst.extend(lst*(int(lst)-1))
    return {i:lst.count(i) for i in set(lst) if i.isalpha()}

shiee小羊子 发表于 2017-9-4 17:28:37

1

咕咕鸡鸽鸽 发表于 2019-1-14 14:59:11

看看

咕咕鸡鸽鸽 发表于 2019-1-14 15:01:11

本帖最后由 咕咕鸡鸽鸽 于 2019-1-14 15:03 编辑

同不会正则{:10_285:}
def fun61(str1):
    #先找出[]()这些括号,分离,先只考虑只有一个括号
    index1 = 0
    index2 = 0
    index3 = 0
    index4 = 0
    dict1 = {}
    if not str1[-1].isdigit():
      str1 += "1"
    for each in str1:
      if not each.isalnum():
            if each == "[":
                index1 = str1.index(each)
            elif each == "]":
                index2 = str1.index(each)
            elif each == "(":
                index3 = str1.index(each)               
            elif each == ")":
                index4 = str1.index(each)

    for each in range(len(str1)):
      #判断,如Mg类的元素
      if str1.isupper() and str1.islower():
            element = str1
            if str1.isdigit():               
                multiple = int(str1)               
            else:
                multiple = 1
      elif str1.isupper() and not str1.islower():
            element = str1
            if str1.isdigit():
                multiple = int(str1)
            else:
                multiple = 1

      #用引索值 得出元素是否在所分离的列表里面
      if element:
            if index1 < each < index2:
                multiple *= int(str1)
            if index3 < each < index4:
                multiple *= int(str1)

            if element not in dict1:
                dict1 = multiple
            else:
                dict1 += multiple
            element = ""
            
    return dict1
   

#fun61("H2O")
#fun61("Mg(OH)2")
print(fun61("K42"))

克里斯保罗 发表于 2019-9-15 11:50:13

咕咕鸡鸽鸽 发表于 2019-1-14 15:01
同不会正则

枯了 不会正则表达式

holiday_python 发表于 2020-6-15 13:20:25

from collections import Counter

def parse_molecule(string):
    c = Counter(string)
    return c

holiday_python 发表于 2020-6-15 22:42:08

可以简单说说思路吗?没看明白,多谢。

19971023 发表于 2020-7-16 21:42:21

1

小陨aoq 发表于 2020-7-30 14:42:33

学习一下
页: [1] 2
查看完整版本: Python: 每日一题 61