neolee / pilot

进入编程世界的第一课
584 stars 840 forks source link

无法理解正则表达式附录的eliza模块中的匹配规则 #1514

Open pyz610173878 opened 1 year ago

pyz610173878 commented 1 year ago

大致了解并练习了本章节的主要语法与用法,打算尝试理解eliza模块。我的思路,直接从头阅读源代码。

但以下代码,尝试简化并拆分。但还是无法理解。 代码如下:

self.keys = list(
map( lambda x: re.compile(x[0],  re.IGNORECASE), gPats))

list函数中嵌套着map函数,其中函数为lambda表达式,参数x,匹配规则为参数x + 集合原子[0]。re.IGNORECASE我查了一下,这个标志允许正则表达式与给定的字符串进行不区分大小写的匹配。

gPats为可迭代对象,以下是gPats具体的值。

gPats = [r'I need (.*)',
  [  "Why do you need %1?",
    "Would it really help you to get %1?",
    "Are you sure you need %1?"]],

老师这一整个嵌套的列套是一个整体的匹配规则吗? 还是说,在逗号分隔的地方为一个匹配规则。我也尝试把此规则放进regex101,但还是不能理解,希望老师给点线索。

neolee commented 1 year ago

这个问题的关键是你要理解 map() 函数的机制,我简单写下,你可能还需要读读相关的文档:

  1. map(f, seq) 接受两个参数,第一个参数是个函数,第二个参数是个列表(其实任何 iterable object 都可以);
  2. map 函数会按顺序用 seq 里的每个元素作为参数去调用函数 f,所以 f 必然是一个单参数的函数;
  3. 调用的结果按照原顺序组成一个新的列表,这个列表就是 map 的返回值。

而你的例子里的函数 f 是什么呢?大致长这样:

def f(x):
    return re.compile(x[0], re.IGNORECASE)

也就是说,f 的作用是取出列表里的每个元素,然后把这个元素(本身又是个列表)的第一个元素拿出来,编译成一个正则匹配对象,并返回。

再来看要被函数 f 反复操作的 gPats 列表,它的结构是这样的:

gPats = [
    [
        r'I need (.*)',
        [
            "Why do you need %1?", "Would it really help you to get %1?",
            "Are you sure you need %1?"
        ]
    ],
    [
        r'Why don\'?t you ([^\?]*)\??',
        [
            "Do you really think I don't %1?", "Perhaps eventually I will %1.",
            "Do you really want me to %1?"
        ]
    ],
    [
        r'Why can\'?t I ([^\?]*)\??',
        [
            "Do you think you should be able to %1?",
            "If you could %1, what would you do?",
            "I don't know -- why can't you %1?", "Have you really tried?"
        ]
    ],
    ...
]

这个列表的每个元素都是一个小列表,每个小列表里有两个元素,第一个是个正则匹配串,比如 r'I need (.*)',所以上面的 map 函数就是把这些正则匹配串取出来组成一个新的列表(保存在 self.keys 中),源代码后面还把 gPats 里每个元素的第二个元素(是个包含若干字符串的列表)取出来,组成了另一个列表(保存在 self.values 中)。

事实上,如果你搞不清楚一些问题,最好的办法是把整个代码放进一个 Jupyter Notebook,分段落执行,看每一步的输出结果,有些问题的答案自己就能猜出来了。

pyz610173878 commented 1 year ago

老师,有个疑惑。已经尝试理解,Eliza模块的基本结构与逻辑。为了追求更深入的理解。是否应该尝试理解为什么它要这么写,或者进一步开始模仿吗?

neolee commented 1 year ago

老师,有个疑惑。已经尝试理解,Eliza模块的基本结构与逻辑。为了追求更深入的理解。是否应该尝试理解为什么它要这么写,或者进一步开始模仿吗?

个人认为更好的思路是,要么自己扔开这个实现自己尝试写个出来(之前研究原代码得到的知识可以用),要么尝试改进原代码实现更多功能,后者更容易一些。