本周在p5p 2000/05/28
- 注释
- 正则表达式引擎增强
eq
和 UTF8 运算符get
*by
函数的缓存- 禁止声明继续
- Perl 新闻
readonly
祈祷符继续- 魔法自增
- 医生,医生,我做这件事的时候很痛!
use strict 'formatting'
- 格式中的复杂表达式
pack("U")
- 各种
注释
您可以订阅此摘要的电子邮件版本,只需向 [email protected]
发送空消息。
请将更正和补充发送到 [email protected]
,其中 YYYYMM
是当前年份和月份。
本周的报告提前了一些,因为明天我必须离开去参加Perl Whirl。下周的报告也会晚些,原因相同。
这个月有很多讨论,其中很多都是相当无意义的。恐怕这不是我们最好的一周。
正则表达式引擎增强
Ben Tilly,Ilya Zakharevich,和 François Désarménien 就正则表达式引擎的替代实现进行了讨论。我将尽可能地简要总结必要的背景。
正则表达式引擎是一个状态机。引擎逐个检查目标字符串中的字符,并对每个字符进行状态转换;在字符串末尾,它检查是否处于“接受状态”,如果是,则模式匹配成功。你得到什么样的正则表达式取决于状态的排列和它们之间的转换。
所有正则表达式引擎面临的基本问题是,某个状态可能在相同的条件下有两个不同的转换。例如,假设你正在匹配 /foo(b*)bar/
并且你已经看到了 foo
。你现在处于一个期望看到 b
的状态。然而,当你看到 b
时,你有两个选择:你可以回到同一个状态并寻找另一个 b
,或者你可以转到下一个状态并寻找 a
。如果字符串是 foobbbar
,则第一个选择是正确的;如果字符串是 foobar
,则第二个选择是正确的。
处理这个问题主要有两种方法。一种方法是使用表示状态机的表示法,该表示法跟踪机器可能同时处于的所有状态。在上面的例子中,它会注意机器可能处于两种可能的状态之一。未来的转换可能会导致对机器实际处于的状态的不确定性。在字符串末尾,引擎只是检查是否 任何 可能的结果状态都是接受状态,如果是,则报告存在匹配。这被称为“DFA”方法。
另一种方法是任意选择其中一个选择,并记住如果匹配不成功,你可以回溯并尝试另一个选择。这被称为“NFA”方法,这正是Perl所做的方法。它选择首先回溯并尝试当前状态,然后再尝试转到下一个状态;这就是Perl的 *
操作符是贪婪的原因。对于非贪婪的 *?
操作符,它首先选择另一个选择。
这两种方法都有优点和缺点。NFA方法的缺点:它在运行时通常更慢。它更难处理非贪婪匹配和反向引用。DFA方法的缺点:它可能在某些正则表达式上花费很长时间,因为它习惯于一次尝试许多等效的替代方案。此外,很难指定你想要最长可能的匹配——考虑编写一个Perl正则表达式来匹配 /elsif|\w+/
中的较长者。如果你使用NFA,这很容易实现。
本的设想是,你从一个NFA开始,然后应用一个众所周知的转换将其转换为DFA。这个众所周知的转换是,DFA中的每个状态对应于原始NFA可能处于的状态集;本的设想是,你可以保留关于状态访问顺序的信息,并使用这些有序的NFA状态集作为单个DFA状态。这种构造的问题在于,在某些情况下,状态的数量会呈指数级爆炸——如果原始NFA有n个状态,则结果DFA可能最多有2n个状态。按照本的想法,这甚至更糟,因为结果DFA可能有最多(n+1)!个状态。但正如本所指出的,在实践中通常表现良好。你将牺牲一个异常的运行时坏行为以换取(可能是)更异常的编译时坏行为。另一方面,如果本的方案在编译时出问题,它会非常糟糕,并且会吃掉你所有的内存。此外,用他的方案处理回引用非常不明确。本对此挥了挥手,伊利亚对此没有想法。
还讨论了各种其他正则表达式问题。本曾建议一个(?\/)
运算符,它匹配空字符串,但阻止回溯;如果引擎尝试回溯过它,则失败。SNOBOL有类似的东西,称为FENCE
。伊利亚说他不想这样做,因为他认为所有有用的用途都已经由(?>...)
覆盖,而且他不想实现一个只能用回溯来解释的功能。他还说,你可以通过以下方式获得这种行为:
(?:|(?{die}))
并将正则表达式匹配放入一个eval
块中。(你明白了吗?它首先尝试匹配空字符串,位于|
的左侧,如果那不行,它尝试匹配(?{die})
,这会抛出异常。)
本建议正则表达式引擎可以有一个钩子,调用一个替代的正则表达式引擎,传递要匹配的字符串和当前位置,子引擎返回一个值,表示它匹配到了多远;这将有助于尝试不同的实现或新功能。
伊利亚花了一些时间讨论他认为可能有用的新功能。一条这样的信息。
当伊利亚提到SNOBOL时,他进入了SNOBOL-berserk模式,并发布了一段关于SNOBOL模式匹配优化的SNOBOL书籍的十二页摘录,这对讨论的其他部分并不特别相关。
Perl 新闻
迪克·哈特被断章取义。这导致了一场非常无聊且离题的倡导讨论。有人要求将这些讨论转移到倡导邮件列表。这意味着他们将讨论cc给两个列表。太棒了。
朋友们,我喜欢倡导列表,因为这样,那些想永远讨论为什么Perl被认为缓慢和臃肿的人就有了一个地方可以去做,而我不必听到它。也许有一天,我会犯下滔天大罪,被判刑每周总结Perl倡导邮件列表上的讨论,我将无法以较轻的罪行换取死刑。在此之前,请帮个忙,把倡导讨论从p5p中排除出去。
医生,医生,我做这件事的时候很痛!
加勒特·戈贝尔
my $val = shift; substr($bitstring,2,4) = pack('p',$val);
伊利亚·扎哈列维奇
不要这样做。希望这能帮到你,伊利亚
eq
和 UTF8 运算符
克拉克·库珀问为什么UTF8字符串不会与字节相同的非UTF8字符串进行eq
比较。
伊利亚回答说,简短的答案是,它们不应该进行比较,因为它们代表不同的字符序列。
他随后进行了详细说明,并表示内部表示形式并不重要;字符串是一系列字符,而字符只是某个范围内的整数。在旧版本的Perl中,范围是0..255;现在范围是0..(2**64-1),这些整数如何实际表示的细节不是应用程序业务的一部分。
get
*by
函数的缓存
上周,Steven Parkes抱怨说,当他使用LWP::UserAgent
模块时,每个新的代理都会调用一个getprotoby
*函数,这会打开/etc/protocols
文件并搜索它。许多代理,许多搜索。他指出,没有方法让LWP::protocol::http
或IO::Socket::inet::_sock_info
(罪魁祸首函数)缓存协议信息。
Ben Tilly建议在这些函数中添加缓存层,或者让标准模块使用缓存的版本。Tom指出,在他机器上未缓存的调用仅需要1⁄6000秒,因此在实践中不太可能成为真正的问题,并且通常缓存很难做到正确。Russ指出,在应用程序级别缓存gethostby
*和getaddrby
*调用是一个非常糟糕的想法,因为这种缓存忽略了DNS记录中的TTL。其他提出的观点:DNS信息已经在named
中正确地缓存了,任何在gethostby
*级别的DNS信息缓存都肯定会出错。
禁止声明继续
上周我抱怨了错误
In string, @X::Y must now be written as \@X::Y...
在随后的讨论中,我建议删除这个错误信息。自1993年以来,我们一直在威胁要
有朝一日它将简单地假设未转义
@
插入了数组。
Sarathy说他认为这应该在两年前就发生了,所以我提供了一个补丁。将未声明的数组插入字符串不再是一个致命错误;相反,如果启用了警告,它将引发警告
Array @X::Y will be interpolated in string
。
readonly
祈祷符继续
Mark Summerfield不喜欢所提出的任何替代方案。有几个基于tie
的解决方案太慢。他不喜欢William Setzer的Const
模块,因为它在运行时发生,所以你必须从你使其成为常量的地方单独声明只读变量。(我怀疑这会起作用?)
const my $x = 12;
他还抱怨说,即使你将标量标记为只读,其他人也可以进入全局作用域并替换整个标量,使用
。
*pi = \3;
有几个人回答说,如果有人非常想改变你的只读值,以至于他们愿意通过篡改符号表来做到这一点,那么他们应该被允许这样做。
Tom发布了他认为是从Larry那里引用的一句话,说
my $PI : constant = 4 * atan2(1,1);
最终会出现的。
魔法自增
Vadim Konovalov抱怨说
$a = 'a';
$a==5;
$a++;
将$a
增加到1而不是增加到b
。这是按文档记录的
自动递增运算符有一些内置的额外魔法。如果你递增一个数字变量,或者它曾经在任何数值上下文中使用过,你将得到正常的递增。
use strict 'formatting'
Ben Tilly建议使用use strict 'formatting'
声明,这样Perl就会在缩进和括号不一致时发出诊断。然而,他没有提供补丁。
格式中的复杂表达式
H. Merijn Brand指出,在格式中,复杂的变量引用,如$e[1]{101}
似乎是非法的。然而,他没有提供补丁。
pack("U")
Meng Wong指出,pack("U")
,如文档所述
[pack] A Unicode character number. Encodes to UTF-8
internally. Works even if C<use utf8> is not in effect.
不生成UTF8字符串。Simon为此提供了一个补丁。然后Ilya、Sarathy和Gisle Aas之间关于这样做是否正确进行了讨论。Gidle说不是,并询问pack("UI")
应该产生什么。Sarathy说"U"
打包一个UTF-8编码的字符,UTF-8使用字节编码;因此pack
的结果应该是字节,而不是另一个UTF-8字符串。Ilya接着问如果有人这样做会发生什么
$a = pack "A20 I", $string, $number;
其中$string
包含大于255的值的字符。Sarathy说"A"
被定义为ASCII,所以它应该失败。然后Ilya指出,如果这样做,将无法将UTF8字符串插入到20字节的固定宽度字段中。讨论继续进行,我无法得出明确的结论。
各种
大量错误报告、错误修复、非错误报告、问题、答案,以及一小部分垃圾邮件。然而没有严重的攻击。
下周见,您的谦卑、顺从的仆人,
标签
反馈
这篇文章有什么问题吗?请通过在GitHub上打开问题或拉取请求来帮助我们。