输入
我有一张非常复杂的单子。
total_aug_rule_path_list =
[[[[['#1_0_0', '#2_0_0', '#3_0_0'], ['#1_0_1', '#2_0_1', '#3_0_1']],
[['#1_0_0', '#2_0_0', '#3_0_0'], ['#1_0_1', '#2_0_1', '#3_0_1']]],
[[['#1_1_0', '#2_1_0', '#3_1_0', '#4_1_0'],
['#1_1_1', '#2_1_1', '#3_1_1', '#4_1_1']]]]]
我有一本字典,把列表中的每个元素都作为关键字。
sym2id_dict = {
'#1_0_0': 1,
'#1_0_1': 2,
'#1_1_0': 3,
'#1_1_1': 4,
'#2_0_0': 5,
'#2_0_1': 6,
'#2_1_0': 7,
'#2_1_1': 8,
'#3_0_0': 9,
'#3_0_1': 10,
'#3_1_0': 11,
'#3_1_1': 12,
'#4_1_0': 13,
'#4_1_1': 14,}
我将把列表中的每个元素映射到字典的值。
输出
[[[[[1, 5, 9], [2, 6, 10]], [[1, 5, 9], [2, 6, 10]]],
[[[3, 7, 11, 13], [4, 8, 12, 14]]]]]
我尝试了以下操作,以尽可能少地使用for循环。
list(map(lambda proofpaths_to_goal :
list(map(lambda proofpaths_to_template :
list(map(lambda proofpath :
list(map(lambda single_augment : list(map(lambda x : sym2id_dict[x], single_augment)),
proofpath)), proofpaths_to_template)), proofpaths_to_goal)),total_aug_rule_path_list))
如果你能告诉我是否有比这种方法更容易或更可读的方法,我将不胜感激。
您可以将列表转换为字符串文字,并使用正则表达式替换字典中的相应字符串,然后将字符串文字转换回列表。
import re
import ast
s = str(total_aug_rule_path_list) #converts to string literal
for element in re.findall(r'#d_d_d', s):
s = s.replace(element, str(sym2id_dict[element]))
s = s.replace("'", "") #because each integer is a string
s = ast.literal_eval(s) #converts string literal back to list, do NOT use eval(s)
print(s)
编辑:请注意,在任何语言(python、perl、js等(中使用eval()
都是危险的,因为这会导致代码注入错误。为了安全起见,请使用ast.literal_eval()
。
输出
[[[[[1, 5, 9], [2, 6, 10]], [[1, 5, 9], [2, 6, 10]]],
[[[3, 7, 11, 13], [4, 8, 12, 14]]]]]
以下是一些替代方案:
嵌套列表理解
[[[[[sym2id_dict[l4] for l4 in l3] for l3 in l2] for l2 in l1] for l1 in l0] for l0 in total_aug_rule_path_list]
尽管可以说这并不容易阅读。
使用Numpy
此方法不适用于示例列表,因为numpy数组不能是不规则的数组(即,所有嵌套相等的列表都必须具有相同的长度(。然而,当你不使用粗糙的数组时,你可以这样做:
import numpy as np
total_aug_rule_path_list = [[
[[['#1_0_0', '#2_0_0', '#3_0_0'], ['#1_0_1', '#2_0_1', '#3_0_1']]],
[[['#1_1_0', '#2_1_0', '#3_1_0'], ['#1_1_1', '#2_1_1', '#3_1_1']]]
]]
sym2id_dict = {...} # your dict here
total_aug_rule_path_list_array = np.array(total_aug_rule_path_list)
print(np.vectorize(sym2id_dict.get)(total_aug_rule_path_list_array))
这将sym2id_dict.get
函数应用于数组中的每个字符串。如果希望它在关键字不在字典中时引发错误,则可以将其更改为sym2id_dict._getitem__
。
编写自己的递归函数
在列表中重复和迭代
此函数重复出现,直到输入不是列表为止。这将适用于[1, [2, 3]]
等列表。如果你想让它处理列表以外的事情,请参阅此处。
def vectorised_apply(f, values):
if isinstance(values,list):
return [vectorised_apply(f,value) for value in values]
else:
return f(values)
print(vectorised_apply(sym2id_dict.get, total_aug_rule_path_list))
固定递归深度
这种变化会重复到固定的深度,因此不需要isinstance
检查:
def vectorised_apply_n(f, values, n=0):
if n == 0:
return f(values)
else:
return [vectorised_apply_n(f, value, n=n-1) for value in values]
print(vectorised_apply_n(sym2id_dict.get, total_aug_rule_path_list, 5))
如果你真的想要的话,你可以用itertools.accumulate
的技巧把这个固定的递归深度函数变成一个表达式,但它很难理解。
from itertools import accumulate
print(
list(accumulate(
range(5), # do 5 times because the list is nested 5 times
initial=lambda x: sym2id_dict[x], # base case: lookup in the dictionary
func=lambda rec, _: lambda xs: [rec(x) for x in xs] # recursive case: build a bigger function using a previous function `rec`
))[-1](total_aug_rule_path_list)) # get the last function from the list