【问题标题】:How to have Variable as Recursive Regex in Perl?如何在 Perl 中将变量作为递归正则表达式?
【发布时间】:2016-08-12 02:16:41
【问题描述】:

我正在为John Tromp's Binary Lambda Calculus 写一个简单的翻译器到 De Bruijn Notation Lambda Calculus,这样我就可以了解他的 Lambda 文件在他的2012 "Most Functional" International Obfuscated C Code winner 中是如何工作的

这里是翻译前的语言示例primes.blc

00010001100110010100011010000000010110000010010001010111110111101001000110100001110011010000000000101101110011100111111101111000000001111100110111000000101100000110110

我在 Bruijn.pl 的 primes.txt 文件保存部分之前的注释行中遇到了嵌套正则表达式的问题:

#!/usr/bin/env perl
#use strict;
use warnings;
use IO::File;
use Cwd; my $originalCwd = getcwd()."/";
#primes.blc as argument for test conversion
#______________________________________________________________________open file
my ($name) = @ARGV;
$FILE = new IO::File;
$FILE->open("< ".$originalCwd."primes.blc") || die("Could not open file!");
#$FILE->open("< ".$name) || die("Could not open file!");
while (<$FILE>){ $field .= $_; }
$FILE->close;
#______________________________________________________________________Translate
$field =~ s/(00|01|(1+0))/$1 /gsm;
$field =~ s/00 /\\ /gsm;
$field =~ s/01 /(a /gsm;
$field =~ s/(1+)0 /length($1)." "/gsme;

$RecursParenthesesRegex = m/\(([^()]+|(??{$RecursParenthesesRegex}))*\)/;
#$field =~ 1 while s/(\(a){1}(([\s\\]+?(\d+|$RecursParenthesesRegex)){2})/\($2\)/sm;
#______________________________________________________________________save file
#$fh = new IO::File "> ".$name;
$fh = new IO::File "> ".$originalCwd."primes.txt";
if (defined $fh) { print $fh $field; $fh->close; }

翻译后的文件primes.txt应该是什么:

\ (\ (1 (1 ((\ (1 1) \ \ \ ((1 \ \ 1) (\ (((4 4) 1) (\ (1 1) \ (2 (1 1)))) \ \ \ \ ((1 3) (2 (6 4)))))) \ \ \ (4 (1 3))))) \ \ ((1 \ \ 2) 2))

当前注释掉的行转换为几乎可读的格式,如下所示:

\ (a \ (a 1 (a 1 (a (a \ (a 1 1 \ \ \ (a (a 1 \ \ 1 (a \ (a (a (a 4 4 1 (a \ (a 1 1 \ (a 2 (a 1 1 \ \ \ \ (a (a 1 3 (a 2 (a 6 4 \ \ \ (a 4 (a 1 3 \ \ (a (a 1 \ \ 2 2 

这需要找到(a 的最内层抽象以及数字或匹配括号中的 2 及其所有内容,并插入尾随 ) 并删除 a 一直到最外层的应用程序。

【问题讨论】:

  • 与其注释掉use strict,不如让你的代码在严格打开的情况下通过。您还有一堆不需要 Time::HiRes。请只显示minimal reproducible example
  • 或许,$RecursParenthesesRegex = m/(?&lt;rec&gt;\((?&gt;[^()]++|(?&amp;rec))*\))/;
  • @simbabque 它是由我的模板 perl 文件制作的,我的错我会编辑帖子。
  • @MJSuriya:如果 OP 为您在源代码中添加行号,则意味着其他所有想要运行代码的人都必须删除它们。第 22 行是以 $field =~ 1 while 开头的那一行
  • 您为什么使用IO::File 而不仅仅是open?并且当它们对模式没有影响时,请不要使用正则表达式 /m/s 修饰符。你的代码已经够复杂了

标签: regex perl recursion nested


【解决方案1】:

你可能需要这样的正则表达式

 # (\(a)(([\s\\]*?(?:\d+|(?&RecursParens))){2})(?(DEFINE)(?<RecursParens>(?>\((?>(?>[^()]+)|(?:(?=.)(?&RecursParens)|))+\))))

 ( \(a )                       # (1)
 (                             # (2 start)
      (                             # (3 start)
           [\s\\]*? 
           (?:
                \d+ 
             |  
                (?&RecursParens) 
           )
      ){2}                          # (3 end)
 )                             # (2 end)

 (?(DEFINE)

      (?<RecursParens>              # (4 start)
           (?>
                \(
                (?>
                     (?> [^()]+ )
                  |  (?:
                          (?= . )
                          (?&RecursParens) 
                       |  
                     )
                )+
                \)
           )
      )                             # (4 end)
 )

使用这样的 Perl 代码

use strict;
use warnings;
use feature qw{say};

my $field = "00010001100110010100011010000000010110000010010001010111110111101001000110100001110011010000000000101101110011100111111101111000000001111100110111000000101100000110110";

$field =~ s/(00|01|(1+0))/$1 /g;
$field =~ s/00 /\\ /g;
$field =~ s/01 /(a /g;
$field =~ s/(1+)0 /length($1)." "/ge;

1 while $field =~ s/(\(a)(([\s\\]*?(?:\d+|(?&RecursParens))){2})(?(DEFINE)(?<RecursParens>(?>\((?>(?>[^()]+)|(?:(?=.)(?&RecursParens)|))+\))))/\($2\)/g;

$field =~ s/\( /\(/g;

say $field;

这会给你这样的输出

\ (\ (1 (1 ((\ (1 1) \ \ \ ((1 \ \ 1) (\ (((4 4) 1) (\ (1 1) \ (2 (1 1)))) \ \ \ \ ((1 3) (2 (6 4)))))) \ \ \ (4 (1 3))))) \ \ ((1 \ \ 2) 2))

可以这样格式化

 \ 
 (                             # (1 start)
      \ 
      (                             # (2 start)
           1 
           (                             # (3 start)
                1 
                (                             # (4 start)
                     (                             # (5 start)
                          \ 
                          ( 1 1 )                       # (6)
                          \ \ \ 
                          (                             # (7 start)
                               ( 1 \ \ 1 )                   # (8)
                               (                             # (9 start)
                                    \ 
                                    (                             # (10 start)
                                         (                             # (11 start)
                                              ( 4 4 )                       # (12)
                                              1
                                         )                             # (11 end)
                                         (                             # (13 start)
                                              \ 
                                              ( 1 1 )                       # (14)
                                              \ 
                                              (                             # (15 start)
                                                   2 
                                                   ( 1 1 )                       # (16)
                                              )                             # (15 end)
                                         )                             # (13 end)
                                    )                             # (10 end)
                                    \ \ \ \ 
                                    (                             # (17 start)
                                         ( 1 3 )                       # (18)
                                         (                             # (19 start)
                                              2 
                                              ( 6 4 )                       # (20)
                                         )                             # (19 end)
                                    )                             # (17 end)
                               )                             # (9 end)
                          )                             # (7 end)
                     )                             # (5 end)
                     \ \ \ 
                     (                             # (21 start)
                          4 
                          ( 1 3 )                       # (22)
                     )                             # (21 end)
                )                             # (4 end)
           )                             # (3 end)
      )                             # (2 end)
      \ \ 
      (                             # (23 start)
           ( 1 \ \ 2 )                   # (24)
           2
      )                             # (23 end)
 )                             # (1 end)

【讨论】:

  • 谢谢@sln!我想你还记得我之前的样子。你是一个正则表达式怪物(以一种好的方式)。
  • @GlassGhost - 很高兴它对你有用。那个 blc 看起来像是带有密钥的密码学。
  • 是的,你应该看看; viruliant.github.iostackoverflow.com/a/21769444/144020 原理相同。我真的相信这是神的语言。
【解决方案2】:

虽然我不懂你的算法,但这行很可疑

$RecursParenthesesRegex = m/\(([^()]+|(??{$RecursParenthesesRegex}))*\)/

您正在根据包含它的模式是否匹配 $_ 来定义一个未声明的变量

use strict 旨在捕捉此类错误,但您并没有修复错误,而是将其关闭。这不明智

猜测您正在尝试定义递归模式,因此您需要使用qr// 而不是m//,并在模式中使用(?0)(?R)

让我们称之为$re 吧?像这样

my $re = qr/\(([^()]+|(?R))*\)/

另外,这条线很奇怪

$field =~ 1 while s/(\(a){1}(([\s\\]+?(\d+|$RecursParenthesesRegex)){2})/\($2\)/sm

它将$field 的值与正则表达式模式1 进行比较,只要替换更改了$_ 中的某些内容,就会丢弃结果

除此之外,如果不描述算法以及您的代码与它的关系,我将无法为您提供帮助

【讨论】:

    猜你喜欢
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-14
    • 1970-01-01
    • 2013-04-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多