请注意此字符串中的两个连续空间:
string = "Hello there everyone!"
for i, c in enumerate(string):
print(i, c)
0 H
1 e
2 l
3 l
4 o
5
6 t
7 h
8 e
9 r
10 e
11
12
13 e
14 v
15 e
16 r
17 y
18 o
19 n
20 e
21 !
如何使列表 len(string)
长,每个值包含单词计数到字符串中的那个点?
预期输出:0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2
我唯一可以做到的是遍历每个字符,设置space=True
标志并每次击中space == True
时都会增加非空间字符时增加计数器。这可能是因为我最精通C,但我想学习一种解决这个问题的方法。
我觉得您的解决方案离Pythonic并不遥远。也许您可以使用zip
操作员迭代两个乘两个,然后才能检测到本地更改(从一个空间到字母 ->这是一个新单词):
string = "Hello there everyone!"
def word_index(phrase):
nb_words = 0
for a, b in zip(phrase, phrase[1:]):
if a == " " and b != " ":
nb_words += 1
yield nb_words
print(list(word_index(string)))
这也利用了在Python中很常见的生成器(请参阅yield
关键字的文档)。您可能可以通过使用itertools.accumulate
而不是for循环来做同样的事情,但是我不确定它不会混淆代码(请参阅Python Zen的第三个项目)。这是它的外观,请注意,我在这里使用了lambda功能,不是因为我认为这是最好的选择,而是因为我找不到任何有意义的函数名称:
import itertools
def word_index(phrase):
char_pairs = zip(phrase, phrase[1:])
new_words = map(lambda p: int(p[0] == " " and p[1] != " "), char_pairs)
return itertools.accumulate(new_words)
第二个版本与第一个版本类似,返回迭代器。请注意,使用迭代器通常是一个好主意,因为它不会对您的用户是否要实例化任何东西做出任何假设。如果用户想将迭代器it
转换为列表,他总是可以像我在第一件代码中一样调用list(it)
。迭代器只会为您一个接一个地提供值:在任何时间点,内存中只有一个值:
for word_index in word_index(string):
print(word_index)
注释phrase[1:]
使该短语的复制,这意味着它可以使使用的内存加倍。可以通过使用返回迭代器的itertools.islice
(因此只使用常数内存)来改善这一点。例如,第二版将看起来像这样:
def word_index(phrase):
char_pairs = zip(phrase, itertools.islice(phrase, 1, None))
new_words = map(lambda p: int(p[0] == " " and p[1] != " "), char_pairs)
return itertools.accumulate(new_words)