鱼C论坛

 找回密码
 立即注册
查看: 5987|回复: 20

[技术交流] Python: 每日一题 61

[复制链接]
发表于 2017-6-7 19:50:21 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 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 = 'K4[ON(SO3)2]2'
parse_molecule(fremySalt)             # return {K: 4, O: 14, N: 2, S: 4}

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


游客,如果您要查看本帖隐藏内容请回复

本帖被以下淘专辑推荐:

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2017-6-7 21:40:46 | 显示全部楼层

                               
登录/注册后可看大图
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2017-6-7 21:41:22 | 显示全部楼层
我叫雷锋,不用谢
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-6-7 21:47:47 | 显示全部楼层

这是你的百度!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2017-6-7 23:48:07 | 显示全部楼层
本帖最后由 SixPy 于 2017-6-7 23:50 编辑
import re
from collections import Counter

def parse_molecule(exp):
        ptns=[re.compile(r'\[([^\[\]]+)\](\d+)'),
              re.compile(r'\(([^()]+)\)(\d+)'),
              re.compile(r'([A-Z][a-z]?)(\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'([A-Z][a-z]?)',r'\1 ',exp).split()
        return dict(Counter(exp))

exp=('H2O','Mg(OH)2','K4[ON(SO3)2]2')
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}
K4[ON(SO3)2]2  {'O': 14, 'N': 2, 'K': 4, 'S': 4}
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2017-6-8 16:08:59 | 显示全部楼层


大神给的答案基本正确,只是在以下几个化学式有点问题。第一个化学式有点特殊,光有括号。后面两个大括弧的化学式我也没见过。
Should parse cyclopentadienyliron dicarbonyl dimer: (C5H5)Fe(CO)2CH3
Should parse really weird molecule: As2{Be4C5[BCo3(CO2)3]2}4Cu5
Should parse hexol sulphate: {[Co(NH3)4(OH)2]3Co}(SO4)3
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2017-6-8 17:02:29 | 显示全部楼层
ooxx7788 发表于 2017-6-8 16:08
大神给的答案基本正确,只是在以下几个化学式有点问题。第一个化学式有点特殊,光有括号。后面两个大括 ...
import re
from collections import Counter

def parse_molecule(exp):
        ptns=[re.compile(r'\{([^{}]+)\}(\d+)'),
              re.compile(r'\[([^\[\]]+)\](\d+)'),
              re.compile(r'\(([^()]+)\)(\d+)'),
              re.compile(r'([A-Z][a-z]?)(\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'([A-Z][a-z]?)',r'\1 ',exp).split()
        return dict(Counter(exp))

exp=('H2O','Mg(OH)2','K4[ON(SO3)2]2','(C5H5)Fe(CO)2CH3',
     'As2{Be4C5[BCo3(CO2)3]2}4Cu5','{[Co(NH3)4(OH)2]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}

K4[ON(SO3)2]2
{'S': 4, 'O': 14, 'K': 4, 'N': 2}

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

As2{Be4C5[BCo3(CO2)3]2}4Cu5
{'As': 2, 'C': 44, 'Co': 24, 'Be': 16, 'B': 8, 'Cu': 5, 'O': 48}

{[Co(NH3)4(OH)2]3Co}(SO4)3
{'H': 42, 'Co': 4, 'N': 12, 'S': 3, 'O': 18}
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2017-6-8 17:30:35 | 显示全部楼层
学习
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2017-6-9 07:59:36 | 显示全部楼层
看看
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2017-6-13 16:38:38 | 显示全部楼层
反正写了一个函数,你上面这几个是没问题,不知道能不能通用
import re

def parse_molecule(f):
  lis = re.findall("[A-Z][a-z]*",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 = 'K4[ON(SO3)2]2'
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}
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2017-6-16 17:46:41 | 显示全部楼层
gopythoner 发表于 2017-6-13 16:38
反正写了一个函数,你上面这几个是没问题,不知道能不能通用

自答,你后面发的那几个复杂的化学式我这个函数解不出来
看来还是不通用啊
这题有点难
其实我老是想着怎么能用eval()方法来解,但是不知道怎么构造出来可以用这个方法的
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 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[i].islower():
            list_s[i] = list_s[i-1]+s[i]
            list_s[i-1] = "" 
    #print(list_s)
    def flatten_lst(lst,m=0): # 拆除括号
        for j in range(len(lst)): 
            if lst[j] in {'[','(','{'}:
                if m==0: start = j # 记录左括号初始位置
                m +=1
            elif lst[j] in {']',')','}'}:
                m -=1
            else:
                continue

            if m==0: # 等于0表明括号配对完成
                if lst[j+1].isdigit():
                    new = lst[start+1:j]*(int(lst[j+1])-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[k].isdigit():
                    lst.extend(lst[k-1:k]*(int(lst[k])-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[1:-len(numbers)]*number,s,1)
        except AttributeError:
            break  # 找不到括号就退出
        
    lst = list(s)
    for i in range(len(s)):
        if s[i].islower():
            lst[i-1], lst[i] = "",lst[i-1]+lst[i]
        if s[i].isdigit():
            lst.extend(lst[i-1:i]*(int(lst[i])-1))
    return {i:lst.count(i) for i in set(lst) if i.isalpha()}
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2017-9-4 17:28:37 | 显示全部楼层
1
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2019-1-14 14:59:11 | 显示全部楼层
看看
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2019-1-14 15:01:11 | 显示全部楼层
本帖最后由 咕咕鸡鸽鸽 于 2019-1-14 15:03 编辑

同不会正则
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[each].isupper() and str1[each+1].islower():
            element = str1[each:each+2]
            if str1[each+2].isdigit():                
                multiple = int(str1[each+2])               
            else:
                multiple = 1 
        elif str1[each].isupper() and not str1[each+1].islower():
            element = str1[each]
            if str1[each+1].isdigit():
                multiple = int(str1[each+1])
            else:
                multiple = 1

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

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

#fun61("H2O")
#fun61("Mg(OH)2")
print(fun61("K4[ON(SO3)2]2"))
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2019-9-15 11:50:13 | 显示全部楼层

枯了 不会正则表达式
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-6-15 13:20:25 | 显示全部楼层
from collections import Counter

def parse_molecule(string):
    c = Counter(string)
    return c
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-6-15 22:42:08 From FishC Mobile | 显示全部楼层
可以简单说说思路吗?没看明白,多谢。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2020-7-16 21:42:21 | 显示全部楼层
1
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2020-7-30 14:42:33 | 显示全部楼层
学习一下
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-1-15 17:24

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表