Skip to content

Commit

Permalink
Fix build error
Browse files Browse the repository at this point in the history
  • Loading branch information
18612295553 committed Feb 21, 2024
1 parent cd84962 commit 7be58c0
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 14 deletions.
2 changes: 1 addition & 1 deletion .markdownlint.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"ol-prefix": false,
"strong-style": false,
"MD033": {
"allowed_elements": ["iframe", "span", "div", "q", "center", "details", "summary"]
"allowed_elements": ["iframe", "span", "div", "q", "center", "details", "summary", "sub", "cite", "u", "del"]
},
"ul-indent": false

Expand Down
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
"AKQJT",
"autoplay",
"blockquote",
"blockquotes",
"floorplan",
"HFLLLF",
"iframe",
"linenos",
"LLLLFH",
"SHDC",
"tablespecs",
Expand Down
4 changes: 2 additions & 2 deletions _posts/2014-12-13-sample-post.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Below is just about everything you'll need to style in the theme. Check the sour

## Heading

```
```plain
# Heading 1
## Heading 2
Expand Down Expand Up @@ -95,7 +95,7 @@ width: 100%;
}
{% endhighlight %}

#### highlight with line number
### highlight with line number

{% highlight ruby linenos %}
def foo
Expand Down
57 changes: 46 additions & 11 deletions _posts/2024-02-21-regex-chatgpt.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ tags: [正则, GPT, AI, Puzzle]
最近在找 ChatGPT 相关的书,偶然看到了这本《Copilot 和 ChatGPT 编程体验:挑战 24 个正则表达式难题》正好学学正则顺便看看别人怎么用 GPT。
看完之后我的评价是正则学了一些,当然这些语法你可以在网上随便搜一下学到,但最后还是有几个有意思的例子值得揣摩。然后是关于 GPT 这部分完全是个噱头<!—-more--->,简短来说 LLM 几乎都解决不了难一些的正则问题,除非这些问题是比较经典的,比如匹配 IPv4 地址。最后说下翻译,没猜错的话应该是拿 GPT 硬翻的,很多地方语句都不通顺,有些编程名词翻译一看就不对,比如代码的 comment 翻译成评论,甚至有的连题目都翻译不清楚,需要靠自己看样例学习。总之,我主要学习正则写法,烂的翻译影响阅读体验但不妨碍学到东西。下面我会列举几个有意思我学到了一些东西的例子。

:tips: 阅读之前我要假设你已经了解大部分的正则语法,包括前行(零宽)断言等高级语法,并且自己从零写过一些正则。

## 难题 6 灾难性回溯

我会改编下这道题,原题有些不必要的字符变换。
Expand Down Expand Up @@ -85,6 +87,7 @@ def is_straight(hand):
def is_four_of_kind(hand):
h = re.sub(r'[^2-9TJQKA]', '', hand) # 只留下点数,其他都删掉
return re.search(h, r'^.?(.)(.*\1){3}')
{% endhighlight %}
```

解释下第三行的正则, `^.?` 用于匹配多余的那一个点数,接着第一个捕获组捕获重复的点数,第二个捕获组 `(.*\1)``\1` 表示第一捕获组,`.*` 用来捕获的多余点数,例如 63666,第二捕获组在第一次(后面有个{3}意思要匹配三次,这次是第一次)会匹配到 36,也相当于有了一个 6。细心的你可能发现这样写对于 6363666 也可以,但别忘了德扑最多 5 张牌。
Expand All @@ -95,7 +98,9 @@ def is_four_of_kind(hand):

## 难题 20 IPv4 地址

这可以说是个经典题目,这里关键在于正则没有表示数值的方式,只能通过字符串的枚举来达到从数字 a 到数字 b 的范围匹配,诀窍是按百十个位依次枚举,不多说直接上答案:
> 经典题目,匹配 IPv4 格式的地址
这里关键在于正则没有表示数值的方式,只能通过字符串的枚举来达到从数字 a 到数字 b 的范围匹配,诀窍是按百十个位依次枚举,不多说直接上答案:

```plain
25[0-5]|2[0-4]\d|[01]?\d\d?
Expand All @@ -105,12 +110,14 @@ def is_four_of_kind(hand):

## 难题 21 匹配倍数序列

匹配¥符号的个数,以空格分隔,后一段要是前一段的两倍,注意这题要求很严格,必须整个串都是这种倍增的关系,例如(注意最后还要有个空格):
> 匹配 ¥ 符号的个数,以空格分隔,后一段必须是前一段的两倍,注意这题要求很严格,必须整个串都是这种倍增的关系。
例如(注意最后还要有个空格 ⎵):

```plain
¥ ¥¥ ¥¥¥¥ ¥¥¥¥¥¥¥¥ // 1 2 4 8 ok
¥¥¥ ¥¥¥¥¥¥ // 3 6 ok
¥ ¥ ¥¥ ¥¥¥¥ // 1 1 2 4 bad
¥ ¥¥ ¥¥¥¥ ¥¥¥¥¥¥¥¥ // 1 2 4 8 ok
¥¥¥ ¥¥¥¥¥¥ // 3 6 ok
¥ ¥ ¥¥ ¥¥¥¥ // 1 1 2 4 bad
```

诀窍在于用先行断言维护【后一段是前一段两倍】这个条件,上答案:
Expand All @@ -123,7 +130,9 @@ def is_four_of_kind(hand):

## 难题 22 斐波那契数列

有了上一题的经验,斐波那契数列匹配应该不是问题,只是这次要用到上一段和上上一段,这里为了可读性,用了多行模式,空格需要用`\`或者`[ ]`写法来和正则的空格区分,答案有点长:
> 有了上一题的经验,斐波那契数列匹配应该不是问题,只是这次要用到上一段和上上一段,判断是否符合斐波那契数列
这里为了可读性,用了多行模式,空格需要用`\`或者`[ ]`写法来和正则的空格区分,答案有点长:

```python
{% highlight python linenos %}
Expand Down Expand Up @@ -153,7 +162,33 @@ def is_fib(s):

`s.split(' ', 1)[1]` 相当于剃掉第一个空格(含)之前的内容,也就是检查原串的所有偶数开始的序列,到此,这个斐波那契数列判断完满解决。

## 难题
## 难题 24 匹配互质

> 假设由空格分隔 ¥ 的个数已经按升序排列,匹配两两互质
例如:

```plain
¥¥ ¥¥¥ ¥¥¥¥¥ ¥¥¥¥¥¥¥ ¥¥¥¥¥¥¥¥¥¥¥ // 2 3 5 7 11 ok
¥¥ ¥¥¥¥¥ ¥¥¥¥¥¥¥¥¥ // 2 5 9 ok
¥¥ ¥¥¥ ¥¥¥¥ // 2 3 4 bad 2,4是合数
```

虽然筛法直接筛质数不行,但这次可以。

诀窍是利用负向前行断言把后面是当前倍数的匹配出来(所以题目要求是升序 小的倍数才能负向匹配到大的),上答案:

```plain
^((¥¥+) (?=\2¥)(?!.* \2{2,} ))+
```

注意这个正则不会捕获最后一个“数字”,它只能判断是否匹配。

## 附录

从最后的几个难题,我重新理解了断言的用法,其实前行断言 指的是右边满足匹配,但不计入匹配,
例如 `(?=[a-z]{3})(.*)` 意思当有三个小写出现时匹配 `.*`仍包括三个小写,后行断言,指的是左边满足匹配。
正向负向,分别指满足匹配和不满足,能和前行后行断言组合,最后就有了 4 种组合形式。

## 番外 斐波那契的完美解

Expand All @@ -165,15 +200,15 @@ def is_fib(s):
{% highlight python linenos %}
pat2 = re.compile(r'''
^(
(?P<g1>@+)[ ] # 匹配第一组
(?P<g1>¥+)[ ] # 匹配第一组
(?= # 开始前行断言
(?P<g2>@+)[ ] # 匹配第二组
(?P<g2>¥+)[ ] # 匹配第二组
((?P=g1)(?P=g2)\ ) # 匹配第三组是第一组接着第二组
)
)+ # 继续循环,注意字符串实际只会消耗第一组,下次消耗第二组,最终只能匹配到倒数第三组,因为前行断言还需要2组
(
(@+)[ ] # 这里已经不需要精确地用g1g2表示了,因为在倒数第三组时已经前行断言过了
(@+)[ ] # 匹配最后一组
(¥+)[ ] # 这里已经不需要精确地用g1g2表示了,因为在倒数第三组时已经前行断言过了
(¥+)[ ] # 匹配最后一组
)
$''',
re.VERBOSE)
Expand Down

0 comments on commit 7be58c0

Please sign in to comment.