Common Lisp:如何使用 CL-PPCRE 模仿 \G 锚点?

问题描述 投票:0回答:1

在正则表达式的 Perl 教程 中,有一个带有

/g
修饰符的示例:

$dna = "ATCGTTGAATGCAAATGACATGAC";
while ($dna =~ /(\w\w\w)*?TGA/g) {  # note the minimal *?
    print "Got a TGA stop codon at position ", pos $dna, "\n";
}

我将其翻译成 Common Lisp,如下所示:

(let ((dna "ATCGTTGAATGCAAATGACATGAC"))
  (ppcre:do-matches (m-s m-e    ; match-start match-end
                     "(\\w\\w\\w)*?TGA"
                     dna
                     nil
                     :start 0 :end (length dna))
    (format t "~&;; Got a TGA stop codon at position ~d"
            m-e)))

其行为类似于 Perl 示例:

;; Got a TGA stop codon at position 18
;; Got a TGA stop codon at position 23
NIL

来自 Perl 教程:

18号位不错,但23号位是假的。发生什么事了?

答案是我们的正则表达式运行良好,直到我们通过 最后一场真正的比赛。那么正则表达式将无法匹配 同步 TGA 并开始向前推进一个字符位置 一次,不是我们想要的。

解决方案是使用 \G 将匹配锚定到密码子 对齐...

CL-PPCRE的文档说

(当前)不支持以下 Perl 功能:... \G 代表 Perl 的 pos() 因为我们没有它。

但是我当然想要这些功能,至少涵盖大部分由

cl-ppcre
完成的教程。所以,我试图弄清楚会发生什么,为什么正则表达式会“开始向前推进一个字符位置”,这是我没想到的,所以我仍然没有真正获得正则表达式的功能。 (但这就是我对我的期望。现在,我尝试找到适合我的 Common Lisp 翻译。然后我将深入研究创建具有所需行为的正则表达式的艺术,以解决各个问题。)

但是,首先我用

ppcre:do-scans
看它:

(let ((dna "ATCGTTGAATGCAAATGACATGAC"))
  (ppcre:do-scans (m-s m-e
                   r-s r-e    ; register-start register-end
                   "(\\w\\w\\w)*?TGA"
                   dna
                   nil
                   :start 0 :end (length dna))
    (format t "~&;; ~a ~a ~a ~a" m-s m-e r-s r-e)))

输出:

;; 0 18 #(12) #(15)     ; end pos 18
;; 20 23 #(NIL) #(NIL)  ; <<<< Why NIL? why start pos 20?
NIL

虽然我很天真,但我首先尝试为我使用这个:

(let ((dna "ATCGTTGAATGCAAATGACATGAC"))
  (ppcre:do-scans (m-s m-e
                   r-s r-e
                   "(\\w\\w\\w)*?TGA"
                   dna
                   nil
                   :start 0 :end (length dna))
    (if (null (elt r-s 0))
        (return)
        (format t "~&;; Got a TGA stop codon at position ~d"
                m-e))))

这完成了这项工作。

;; Got a TGA stop codon at position 18
NIL

但没想到这会是一个通用的解决方案。

在“DNA”中添加三胞胎又改变了图景(当然是弗兰肯斯坦):

(let ((dna "ATCGTTGAATGCAAATGACATGACTGCTGAGTTATGAAATGCATC"))
  (ppcre:do-scans (m-s m-e
                   r-s r-e
                   "(\\w\\w\\w)*?TGA"
                   dna
                   nil
                   :start 0 :end (length dna))
    (format t "~&;; ~a ~a ~a ~a" m-s m-e r-s r-e)))

结果:

;; 0 18 #(12) #(15)
;; 18 30 #(24) #(27)     <<<<<< end 30
;; 31 37 #(31) #(34)     <<<<<< start 31 results in a bogus
NIL

可以肯定的是(并希望找到“向前迈出一个字符”的必要性的提示),我将其想象为这样:

;;  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17|18
;;  A  T  C  G  T  T  G  A  A  T  G  C  A  A  A  T  G  A| 
;;         |        |        |        |        |        |

;;                                      v
;; 18 19 20 21 22 23 24 25 26|27 28 29|30<
;;  C  A  T  G  A  C  T  G  C  T  G  A| G
;;         |        |        |        | ∧ 
                                         
;;  v
;; 31 32 33 34 35 36 37 38 39 40 41 42 43 44
;;  T  T  A  T  G  A| A  A  T  G  C  A  T  C
;;      |        |  |     |        |
;; |-------|--------|
;;       bogus

既然我想,也许我可以从更长的时间中获得更多信息 顺序,我又试了一次:

(let ((dna "ATCGTTGAATGCAAATGACATGACTGCTGAGTTATGAAATGCATCTGCTGAATCAAACTGAAATGAATC"))
  (ppcre:do-scans (m-s m-e
                   r-s r-e
                   "(\\w\\w\\w)*?TGA"
                   dna
                   nil
                   :start 0 :end (length dna))
    (format t "~&;; ~a ~a ~a ~a" m-s m-e r-s r-e)))

结果是:

;; 0 18 #(12) #(15)
;; 18 30 #(24) #(27)    end pos 30
;; 30 51 #(45) #(48)    <<<<<<< oops, start pos 30 and the match is correct
;; 51 66 #(60) #(63)    <<<<<<< again correct.
NIL

(再次,我想真正确定并完成我的可视化。但我毫不怀疑你不想再看到它。)

这张图片与更长的“DNA”保持不变

"ATCGTTGAATGCAAATGACATGACTGCTGAGTTATGAAATGCATCTGCTGAATCAAACTGAAATGAATCAAATGCTGACCC"

输出

;; 0 18 #(12) #(15)
;; 18 30 #(24) #(27)
;; 30 51 #(45) #(48)
;; 51 66 #(60) #(63)
;; 66 78 #(72) #(75)
NIL

而且我觉得:对应的效果还是模仿的

\G
锚点就是告诉
ppcre:do-scans
,在这种情况下 搜索它总是应以它在中的方式进行 最后两个案例的字符串很长。

但我现在不知道该怎么做或如何告诉它

ppcre:do-scans
和它的亲戚。 (我还没有找到为什么我们需要
"(\\w\\w\\w)*?TGA"
“开始一次向前迈出一个角色位置”的模式。)

并且:如果我从 pos 18 -> pos 20 和 pos 30 -> pos 31 开始,然后继续 pos 30 -> pos 30、pos 51 -> pos 51,我会得到 pos 567 -> pos 566 的长度吗?位置 1038 -> 位置 1036 等等?

你能帮我吗?

regex perl common-lisp
1个回答
0
投票

所以,我试图弄清楚会发生什么,为什么正则表达式会“开始向前推进一个字符位置”,这是我没想到的,所以我仍然没有真正理解正则表达式的功能。

首先,让我们在您的程序中添加一些内容:

$dna = "ATCGTTGAATGCAAATGACATGAC";
while ($dna =~ /(\w\w\w)*?TGA/g) {  # note the minimal *?
    print "Matched found at [$-[0],$+[0]): `$&`.\n";
}
Matched found at [0,18): `ATCGTTGAATGCAAATGA`.
Matched found at [20,23): `TGA`.

$+[0]
pos($dna)
相同。)

在 Perl 中,默认情况下不锚定正则表达式。我的意思是,如果在第一个位置找不到匹配项,它将尝试从第二个位置开始找到匹配项。它会迭代地执行此操作,直到找到匹配项,或者已尝试过每个后续位置。

例如,即使字符串不以

b
开头,以下内容也会返回 true(在标量上下文中计算时)。

"abc" =~ /b/   # true

查看您的程序,尝试以 18 号位置进行比赛,但失败了。于是它尝试了19号位置,但也失败了。但它确实成功地在第 20 位找到了匹配。


那么你能做些什么呢?我不知道。我不熟悉 List 或 CL-PPCRE。

知道每个模式都以

\G(?s:.)*?\K
为前缀可能会很有用。

/\G(?s:.)*?\Kb/                # Same as /b/
/\G(?s:.)*?\K(\w\w\w)*?TGA/g   # Same as /(\w\w\w)*?TGA/g
© www.soinside.com 2019 - 2024. All rights reserved.