0

我刚刚开始学习 python,我有一个让我困惑的练习:创建一个可以打包或解包一串字母的函数。所以 aaabb 将被打包 a3b2 ,反之亦然。

对于函数的打包部分,我写了以下

def packer(s):
    if s.isalpha():        # Defines if unpacked

    stack = []

        for i in s:
            if s.count(i) > 1:
                if (i + str(s.count(i))) not in stack:
                    stack.append(i + str(s.count(i)))
            else:
                stack.append(i)

        print "".join(stack)

    else:
        print "Something's not quite right.."
        return False
packer("aaaaaaaaaaaabbbccccd")

这似乎一切正常。但是作业说如果输入(例如)在 b 或 c 之后有字母 a,那么它应该稍后被解压缩成它的原始形式。所以“aaabbkka”应该变成a3b2k2a,而不是a4b2k2。因此我想,我不能使用“count()”命令,因为它会计算整个字符串中所有出现的项目,对吗?那么我在这里的选择是什么?

开始拆包——我已经想到了我的代码需要做的基础知识——

  1. 在“ if s.isalpha():” 和 else 之间,我应该添加一个 elif 来检查字符串中是否包含数字。(我认为这足以确定它是打包版本还是未打包版本)。
  2. 创建一个 for 循环并在其中创建一个 if 语句,然后检查每个元素:

    2.1。如果它后面有一个数字 > 返回(或添加到一个空堆栈)该数字乘以数字
    2.2。如果它后面没有数字 > 只返回元素。

大问题 2 - 我如何检查它是数字还是列表中元素后面的另一个字母元素?我想这必须通过切片来完成,但那些只取整数。这可以通过 index 命令实现吗?

另外 - 如果这有任何相关性 - 到目前为止,我基本上已经涵盖了列表、字符串、if 和 for 并且我被告知这个练习只对那些是可行的(......所以如果你不介意保持这个真的基本的)

感谢新手爱好者的所有帮助!


解决了:

def packer(s):
    if s.isalpha():        # Defines if unpacked
        groups= []
        last_char = None

        for c in s:
            if c == last_char:
                groups[-1].append(c)
            else:
                groups.append([c])
            last_char = c

        return ''.join('%s%s' % (g[0], len(g)>1 and len(g) or '') for g in groups)

    else:                   # Seems to be packed

        stack = ""

        for i in range(len(s)):
            if s[i].isalpha():
                if i+1 < len(s) and s[i+1].isdigit():
                    digit = s[i+1]
                    char = s[i]
                    i += 2

                    while i < len(s) and s[i].isdigit():
                        digit +=s[i]
                        i+=1
                    stack += char * int(digit)

                else:
                    stack+= s[i]
            else:
                ""
        return "".join(stack) 
print (packer("aaaaaaaaaaaabbbccccd"))
print (packer("a4b19am4nmba22")) 

所以这是我的最终代码。几乎只用 for 循环和 if 语句就能完成这一切。最后,虽然我不得不引入 while 循环来解决读取多位数字的问题。我想我仍然设法保持它足够简单。感谢一吨毫驼鹿和其他所有人的参与!

4

3 回答 3

2

我认为您可以使用“itertools.grouby”功能

例如

import itertools
data = 'aaassaaasssddee'
groupped_data = ((c, len(list(g))) for c, g in itertools.groupby(data))
result = ''.join(c + (str(n) if n > 1 else '') for c, n in groupped_data)

当然可以使用生成器而不是生成器语句使这段代码更具可读性

于 2013-09-22T11:43:43.300 回答
2

一个简单的解决方案:如果一个字符不同,则创建一个新组。否则将其附加到最后一个组。最后计算所有组并加入他们。

def packer(s):
    groups = []
    last_char = None
    for c in s:
        if c == last_char:
            groups[-1].append(c)
        else:
            groups.append([c])
        last_char = c
    return ''.join('%s%s'%(g[0], len(g)) for g in groups)

另一种方法是使用re.

正则表达式r'(.)\1+'可以匹配长于 1 的连续字符。re.sub您可以轻松地对其进行编码:

regex = re.compile(r'(.)\1+')

def replacer(match):
    return match.group(1) + str(len(match.group(0)))

regex.sub(replacer, 'aaabbkka')
#=> 'a3b2k2a'
于 2013-09-22T12:32:59.317 回答
2

这是我在评论中概述的算法的实现:

from itertools import takewhile, count, islice, izip

def consume(items):
    from collections import deque
    deque(items, maxlen=0)

def ilen(items):
    result = count()
    consume(izip(items, result))
    return next(result)

def pack_or_unpack(data):
    start = 0
    result = []

    while start < len(data):
        if data[start].isdigit():
            # `data` is packed, bail
            return unpack(data)
        run = run_len(data, start)

        # append the character that might repeat
        result.append(data[start]) 

        if run > 1:
            # append the length of the run of characters
            result.append(str(run))

        start += run

    return ''.join(result)


def run_len(data, start):
    """Return the end index of the run of identical characters starting at 
    `start`"""
    return start + ilen(takewhile(lambda c: c == data[start], 
                                  islice(data, start, None)))

def unpack(data):
    result = []
    for i in range(len(data)):
        if data[i].isdigit():
            # skip digits, we'll look for them below
            continue

        # packed character
        c = data[i]
        # number of repetitions
        n = 1
        if (i+1) < len(data) and data[i+1].isdigit():
            # if the next character is a digit, grab all the digits in the 
            # substring starting at i+1
            n = int(''.join(takewhile(str.isdigit, data[i+1:])))

        # append the repeated character
        result.append(c*n) # multiplying a string with a number repeats it
    return ''.join(result)

print pack_or_unpack('aaabbc')
print pack_or_unpack('a3b2c')
print pack_or_unpack('a10')
print pack_or_unpack('b5c5')
print pack_or_unpack('abc')

正则表达式风格的版本unpack()将是:

import re
UNPACK_RE = re.compile(r'(?P<char> [a-zA-Z]) (?P<count> \d+)?', re.VERBOSE)
def unpack_re(data):
    matches = UNPACK_RE.finditer(data)
    pairs = ((m.group('char'), m.group('count')) for m in matches)
    return ''.join(char * (int(count) if count else 1)
                   for char, count in pairs)

此代码演示了实现该算法的最直接(或“基本”)方法。它不是特别优雅或惯用或一定有效。(如果是用 C 编写的,那会是这样,但 Python 有一些警告,例如:索引字符串会将字符复制到新字符串中,如果复制完成,似乎过度复制数据的算法可能比试图避免这种情况更快C 和解决方法是使用 Python 循环实现的。)

于 2013-09-22T13:15:22.880 回答