Skip to content

Commit

Permalink
Finished 'scripts/lua/pattern_matching.md'.
Browse files Browse the repository at this point in the history
  • Loading branch information
gnu4cn committed Nov 10, 2023
1 parent 3a2c69d commit 6f35275
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 0 deletions.
17 changes: 17 additions & 0 deletions scripts/lua/tricks.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,20 @@ function nocase (s)
end

print(nocase("Hi there!"))


function code (s)
return (string.gsub(s, "\\(.)", function (x)
return string.format("\\%03d", string.byte(x))
end))
end

function decode (s)
return (string.gsub(s, "\\(%d%d%d)", function (d)
return "\\" .. string.char(tonumber(d))
end))
end

s = [[follows a typical string: "This is \"greate\"!"]]

print(decode(string.gsub(code(s), '".-"', string.upper)))
98 changes: 98 additions & 0 deletions src/lua_tut/pattern_matching.md
Original file line number Diff line number Diff line change
Expand Up @@ -673,4 +673,102 @@ s1 = string.gsub(s1, "(%W)", "%%%1")
s2 = string.gsub(s2, "%%", "%%%%")
```

在检索字符串中,我们转义了所有非字母数字字符(因此是大写的 `W`)。而在替换字符串中,我们只转义了百分号。

另一种有用的模式匹配技巧,是在真正工作之前,对主题字符串加以预处理。假设我们打算将文本中所有带引号的字符串,都改为大写,其中引号字符串以双引号(`"`)开始和结束,但也可能包含转义的引号(`"\""`):


```text
follows a typical string: "This is \"great\"!"
```

处理这种情况的一种方法,便是对文本加以预处理,将有问题的序列,编码为其他序列。例如,我们可以把 `"\""` 编码为 `"\1"`。但是,如果原文中已经包含了一个 `"\1"`,我们就有麻烦了。要进行编码并避免这个问题,一个简单的方法,是将所有 `"\x"` 序列,编码为 `"\ddd"`,其中 `ddd` 是字符 `x` 的十进制表示法:


```lua
function code (s)
return (string.gsub(s, "\\(.)", function (x)
return string.format("\\%03d", string.byte(x))
end))
end
```

现在,编码字符串中的任何序列 `"\ddd"`,都必定来自编码,因为原始字符串中的任何 `"\ddd"`,也已经被编码了。因此,解码就是一件很容易的事:


```lua
function decode (s)
return (string.gsub(s, "\\(%d%d%d)", function (d)
return "\\" .. string.char(tonumber(d))
end))
end
```

现在我们就可以完成任务了。由于编码字符串不再包含任何转义了引号(`"\""`),我们只需使用 `'".-"'`,即可检索到带引号的字符串:


```lua
s = [[follows a typical string: "This is \"greate\"!"]]

s = code(s)
s = string.gsub(s, '".-"', string.upper)
s = decode(s)
print(s) --> follows a typical string: "THIS IS \"GREATE\"!"
```

我们也可以这样写:

```lua
print(decode(string.gsub(code(s), '".-"', string.upper)))
```

模式匹配函数对 UTF-8 字符串的适用性取决于模式。由于 UTF-8 任何字符的编码,都不会出现在任何其他字符的编码中这一关键特性,因此字面模式的运行不会出现问题。字符类和字符集,character classes and character sets,只适用于 ASCII 字符。例如,`"%s"` 模式会工作于 UTF-8 字符串,但他只会匹配 ASCII 空格,而不会匹配额外的 Unicode 空格,如非断开空格,a non-break space(`U+00A0`)或蒙古语的元音分隔符(`U+180E`)。

明智的模式,judicious patterns,可以为 Unicode 处理,带来一些额外功能。一个很好的例子,便是预定义模式 `utf8.charpattern`,他可以精确匹配一个 UTF-8 字符。`utf8` 库将此模式定义如下:


```lua
utf8.charpattern = [\0-\x7F\xC2-\xF4][\x80-\xBF]*
```

其中的第一部分,是一个匹配 ASCII 字符(范围 `[0, 0x7F]`),或多字节序列初始字节(范围 `[0xC2, 0xF4]`)的类。第二部分会匹配零或多个后续字节(范围 `[0x80,0xBF]`)。


## 练习

练习 10.1:请编写一个函数,他会取一个字符串,和一个分隔符模式,并返回其中包含由分隔符分隔出的、原始字符串中块的表:


```lua
t = split("a whole new world", " ")
-- t = {"a", "whole", "new", "world"}
```

练习 10.2:模式 `"%D"``"[^%d]"` 是等价的。那么模式 `"[^%d%u]"``"[%D%U]"` 呢?


练习 10.3:请编写一个函数 `transliterate`。该函数要取一个字符串,并根据作为第二个参数所给到的表,将字符串中的每个字符,都替换为另一个字符。如果表将 `"a"` 映射为 `"b"`,函数应将出现的任何 `"a"`,都替换为 `"b"`;如果表将 `"a"` 映射为 `false`,函数应从结果字符串中,删除出现的 `"a"`


练习 10.4: 在 [“捕获物”](#捕获物) 小节的结尾,我们定义了一个 `trim` 函数。由于使用了回溯,backtracking,因此对于某些字符串,该函数可能需要花费次方的时间,`O(n^2)`(例如,在我的新机器上,匹配一个 100 KB 的字符串,可能需要 52 秒。)

- 请创建一个会触发函数 `trim` 中,这种次方行为的字符串;

- 请重写该函数,使其始终以线性时间运行。


练习 10.5:请写一个函数,将二进制字符串,格式化为 Lua 中的字面形式,所有字节都使用转义序列 `\x`


```lua
print(escape("\0\1hello\200"))
--> \x00\x01\x68\x65\x6C\x6C\x6F\xC8
```

作为改进版,还要使用转义序列 `\z`,来中断长的行。


练习 10.6:请为 UTF-8 字符,重写函数 `transliterate`


练习 10.7:请编写一个反转 UTF-8 字符串的函数。

0 comments on commit 6f35275

Please sign in to comment.