假设不存在以
!
开始和结束的元素,例如
'!foo!'
。
首先,我们可以编写辅助谓词,例如:
def is_starting_element(element):
return element.startswith('!')
def is_ending_element(element):
return element.endswith('!')
那么我们可以编写生成器函数(因为它们很棒)
def walk(elements):
elements = iter(elements)
for position, element in enumerate(elements):
if is_starting_element(element):
yield [element[1:], *walk(elements)]
elif is_ending_element(element):
yield element[:-1]
return
else:
yield element
测试:
>>> lst = ['foo', 'bar', '!test', 'hello', 'world!', 'word']
>>> list(walk(lst))
['foo', 'bar', ['test', 'hello', 'world'], 'word']
>>> lst = ['foo', 'bar', '!test', '!hello', 'world!', 'word!']
>>> list(walk(lst))
['foo', 'bar', ['test', ['hello', 'world'], 'word']]
>>> lst = ['hello!', 'world!']
>>> list(walk(lst))
['hello']
正如我们从上一个例子中看到的那样,如果剩余的结束元素多于开始元素,则剩余的结束元素将被忽略(这是因为我们从生成器中 return
)。因此,如果lst
具有无效签名(打开和关闭元素之间的差异不等于零),那么我们可能会遇到一些不可预测的行为。为了摆脱这种情况,我们可以在处理数据之前验证给定的数据并在数据无效时引发错误。
我们可以编写类似以下的验证器:
def validate_elements(elements):
def get_sign(element):
if is_starting_element(element):
return 1
elif is_ending_element(element):
return -1
else:
return 0
signature = sum(map(get_sign, elements))
are_elements_valid = signature == 0
if not are_elements_valid:
error_message = 'Data is invalid: '
if signature > 0:
error_message += ('there are more opening elements '
'than closing ones.')
else:
error_message += ('there are more closing elements '
'than opening ones.')
raise ValueError(error_message)
测试
>>> lst = ['!hello', 'world!']
>>> validate_elements(lst)
>>> lst = ['!hello', '!world']
>>> validate_elements(lst)
...
ValueError: Data is invalid: there are more opening elements than closing ones.
>>> lst = ['hello!', 'world!']
>>> validate_elements(lst)
...
ValueError: Data is invalid: there are more closing elements than opening ones.
最后,我们可以编写带有验证的函数,例如:
def to_sublists(elements):
validate_elements(elements)
return list(walk(elements))
测试
>>> lst = ['foo', 'bar', '!test', 'hello', 'world!', 'word']
>>> to_sublists(lst)
['foo', 'bar', ['test', 'hello', 'world'], 'word']
>>> lst = ['foo', 'bar', '!test', '!hello', 'world!', 'word!']
>>> to_sublists(lst)
['foo', 'bar', ['test', ['hello', 'world'], 'word']]
>>> lst = ['hello!', 'world!']
>>> to_sublists(lst)
...
ValueError: Data is invalid: there are more closing elements than opening ones.
编辑
如果我们想要处理以!
开始和结束的元素,例如'!bar!'
,我们可以使用itertools.chain
来修改walk
函数。如下:
from itertools import chain
def walk(elements):
elements = iter(elements)
for position, element in enumerate(elements):
if is_starting_element(element):
yield list(walk(chain([element[1:]], elements)))
elif is_ending_element(element):
element = element[:-1]
yield element
return
else:
yield element
此外,我们需要通过修改 get_sign
函数来完成验证。
def get_sign(element):
if is_starting_element(element):
if is_ending_element(element):
return 0
return 1
if is_ending_element(element):
return -1
return 0
测试
>>> lst = ['foo', 'bar', '!test', '!baz!', 'hello', 'world!', 'word']
>>> to_sublists(lst)
['foo', 'bar', ['test', ['baz'], 'hello', 'world'], 'word']
!
之间的所有元素吗? - Ollie!
开头和结尾,例如'!element!'
? - Azat Ibrakov