首页 > 解决方案 > 将 re.sub 应用于列表 If 字典值 == x

问题描述

编辑 2020 年 12 月 9 日:有人要求我澄清这个问题。我能提供的最好的说明是 ShadowRanger 的代码:


    for k, cnt in stock.items(): 
        stage = [s for s in stage if s.count(k) <= cnt]

...做了我想要实现的目标以及更多(消除其他现在冗余的代码)。

我有一个有限的字母“库存”(想想拼字游戏瓷砖),需要消除数组“阶段”中无法从库存中构建的单词。

我的 Perl 示例处理这样一种情况,即我有一个字符的“库存”和一个包含需要两个或多个该字符的单词的“阶段”,并将这些单词标记为删除(更准确地说是为了从以后的 grep 中排除到最终数组)。

ShadowRanger 的代码涵盖所有 stock=n, required>n ...完美的情况。

对不起,我搞砸了我的解释:下次会更加努力。

克里斯


Python 新手试图做一些在 Perl 中有效但找不到 Pythonic 等价物的事情。

我有一个字典 char: available count of char

stock = {'p': 1, 'r': 2, 't': 1}

和一个清单:

stage = ['pap', 'pal', 'pat', 'pop', 'pot', 'tap', 'tat', 'top']

我想要做的是消除舞台中无法从stock构造的任何字符串。这是从舞台上消除“不值得”候选人的几个步骤之一。目前的计划是用“?”替换不值得的字符串。包含“?”的字符串 当阶段被发送到其最终目的地时将被过滤掉。目标——在阶段:'pap' -> '?' '流行'->'?...ETC

Perl:工作

$badchar = '?';
foreach my $key (keys %stock){
   if ($stock{$key} == 1){
      grep (s/^.*$key.*$key/$badchar/, @stage);
   }
}

Python:不工作

    for k in stock:
      if stock[k] == 1:
        pattern = 'r".*' + k + '.*' + k + '.*"'
        replacement = '?'
        for w in stage:
          w = re.sub(pattern, replacement, w)

我在 Python 中尝试了十几种其他方法……这是第一种不会引发错误的方法,但它也不能替代!

其他阶段的修剪步骤有效,这个没有!在我的(简单)测试用例中,在尝试此步骤之前,我的阶段性条目从 25 万个减少到 210 个左右。这个修剪步骤可能需要减少到 190 个条目,所以我可以简单地放弃这个任务,但它应该在 Python 中是可行的。这是一个过程的几秒钟设置阶段的一部分,可能需要一个小时或更长时间才能完成,因此: 快速工作并不重要;作品不错;工作和优雅是最好的。

编辑:回应我的“说明性”数据不足。

这是一个真实的案例:说明性的“P”示例在这种情况下不起作用(有 2 个库存),但请考虑'TAT', 'TET', 'TIT', 'TOT''TUT'已从阶段中删除)。

stock = {'C': 1, 'A': 3, 'P': 2, 'O': 1, 'T': 2, 'H': 1, 'E': 2, 'I': 1, 'N': 1, 'M': 1, 'S': 1}

如果你有耐心等待很长一段时间,这里是和上面一样的真实案例:

stage = ['ACE', 'ACT', 'AHA', 'AHS', 'AIA', 'AIM', 'AIN', 'AIS', 'AIT', 'AMI', 'AMP', 'ANA', 'ANE', 'ANI', 'ANN', 'APE', 'APT', 'ASH', 'ASP', 'ASS', 'ATE', 'ATS', 'CAM', 'CAN', 'CAT', 'CEE', 'CEP', 'CHA', 'CHE', 'CHI', 'CIT', 'CON', 'COO', 'COP', 'COS', 'COT', 'EAN', 'EAS', 'EAT', 'EEN', 'EHS', 'EME', 'EMS', 'ENA', 'ENE', 'ENS', 'EON', 'ESS', 'EST', 'ETA', 'ETH', 'HAE', 'HAH', 'HAN', 'HAP', 'HAS', 'HAT', 'HEM', 'HEN', 'HEP', 'HES', 'HET', 'HIC', 'HIE', 'HIM', 'HIN', 'HIP', 'HIS', 'HIT', 'HOA', 'HOC', 'HOE', 'HOH', 'HOI', 'HON', 'HOO', 'HOP', 'HOS', 'HOT', 'ICE', 'ICH', 'IMP', 'INA', 'INN', 'INS', 'ION', 'IOS', 'ISH', 'ISM', 'ITA', 'ITS', 'MAA', 'MAC', 'MAE', 'MAM', 'MAN', 'MAO', 'MAP', 'MAT', 'MEN', 'MET', 'MHO', 'MIA', 'MIM', 'MIS', 'MNA', 'MOA', 'MOE', 'MOM', 'MON', 'MOO', 'MOP', 'MOT', 'NAE', 'NAH', 'NAM', 'NAN', 'NAP', 'NAS', 'NAT', 'NEE', 'NEO', 'NEP', 'NET', 'NIM', 'NIP', 'NIS', 'NIT', 'NOH', 'NON', 'NOS', 'NOT', 'NTH', 'OAT', 'OCA', 'OCH', 'OES', 'OHM', 'OHO', 'OHS', 'OIS', 'OMS', 'ONE', 'OOH', 'OOM', 'OON', 'OOS', 'OPE', 'OPS', 'PAH', 'PAM', 'PAN', 'PAP', 'PAS', 'PAT', 'PEA', 'PEC', 'PEE', 'PEI', 'PEP', 'PET', 'PHI', 'PHO', 'PIA', 'PIC', 'PIE', 'PIN', 'PIP', 'PIS', 'PIT', 'POA', 'POE', 'POH', 'POI', 'POM', 'PON', 'POO', 'POP', 'POS', 'POT', 'PSI', 'SAC', 'SAE', 'SAI', 'SAM', 'SAN', 'SAP', 'SAT', 'SEA', 'SEC', 'SEE', 'SEI', 'SEN', 'SET', 'SHE', 'SHS', 'SIC', 'SIM', 'SIN', 'SIP', 'SIS', 'SIT', 'SOC', 'SOH', 'SON', 'SOP', 'SOS', 'SOT', 'SPA', 'STS', 'TAE', 'TAI', 'TAM', 'TAN', 'TAO', 'TAP', 'TAS', 'TAT', 'TEA', 'TEC', 'TEE', 'TEN', 'TET', 'THO', 'TIC', 'TIM', 'TIN', 'TIP', 'TIS', 'TIT', 'TOC', 'TOE', 'TOM', 'TON', 'TOO', 'TOP', 'TOT']

嗯,显然还不清楚——再试一次。

考虑以“T”开头的单词

'TUT' 已被较早的过滤器(库存中的零'U')消除。

'TAT', 'TET', 'TIT', TOT' 必须消除 - 每个都需要 2*'T' 并且只有 1*'T' 有库存。

类似地,'INN'(库存中只有 1*'N')、'OON' ......可能还有其他我没有注意到的

标签: python

解决方案


继续使用正则表达式的解决方案(见下文以获得更好的方法)

pattern = 'r".*' + k + '.*' + k + '.*"'

没有做你想做的事。您似乎希望它是一个原始字符串文字,但您只是在制作一个以 . 开头r"和结尾的常规字符串"。如果您想制作一个原始的实际模式,您可以执行以下操作:

pattern = fr'.*{k}.*{k}.*'  # Change k to re.escape(k) in each case if it might contain regex specials

您还需要替换stage; Perl 有可变字符串,Python 没有,所以你不能在不存储结果(一个全新的字符串)的情况下应用替换,在这种情况下,用结果替换原始字符串。一个简单的解决方案是stage批量替换 listcomp,得到最终代码:

import re
stock = {'p': 1, 'r': 2, 't': 1}
stage = ['pap', 'pal', 'pat', 'pop', 'pot', 'tap', 'tat', 'top']
for k in stock:
    if stock[k] == 1:
        pattern = fr'.*{k}.*{k}.*'
        replacement = '?'
        stage = [re.sub(pattern, replacement, w) for w in stage]  # List comp to replace
        print(stage)

没有正则表达式的解决方案

如果目标很简单,如“删除stock映射到的值在字符串中1出现两次或多次的所有字符串”,则可以在不使用正则表达式的情况下执行此操作(并且无需替换以生成新字符串,只需将现有字符串过滤掉):

for k, cnt in stock.items(): # Avoid lookup of stock[k] by getting paired key/value
    if cnt == 1:
        # Throws away "bad" strings instead of replacing them with '?':
        stage = [s for s in stage if s.count(k) < 2]

        # If you really want the '?' strings preserved:
        stage = [s if s.count(k) < 2 else '?' for s in stage]

在看不到正则表达式的情况下一次性删除“坏”字符串。在将 Perl 转换为 Python 时,这是很常见的情况;Perl 对除了几个非常简单的情况(chomp, chop, substr)之外的所有事情都使用正则表达式,而 Python 提供的方法首先减少了对正则表达式的需求。

看起来您可能打算删除stock字符超过其相关计数次数的任何内容(因此对于stock[k] == 1,您将消除k重复两次或更多次的字符串,对于stock[k] == 2,三次或更多次等),所以使它应用该过滤器可以让您:

for k, cnt in stock.items(): # Avoid lookup of stock[k] by getting paired key/value
    # Throws away "bad" strings instead of replacing them with '?':
    stage = [s for s in stage if s.count(k) <= cnt]

    # If you really want the '?' strings preserved:
    stage = [s if s.count(k) <= cnt else '?' for s in stage]

其中s.count(k) <= cnt使用dict'scnt作为重复限制。


推荐阅读