什么是;选择((选择,$|=1)[0])”;你喜欢Perl吗?

什么是;选择((选择,$|=1)[0])”;你喜欢Perl吗?,perl,select,buffering,Perl,Select,Buffering,我见过一些用Perl编写的可怕代码,但我无法理解这一点: select((select(s),$|=1)[0]) 它存在于我们用来与服务器通信的一些网络代码中,我认为这与缓冲有关(因为它设置了$|) 但我不明白为什么会有多个select调用或数组引用。有人能帮我吗?这是一个讨厌的小习惯用法,用于在标准输出以外的文件句柄上设置自动刷新 select()获取提供的文件句柄并(基本上)用它替换STDOUT,完成后返回旧的文件句柄 因此(选择($s),$|=1)重定向文件句柄(记住select返回旧的

我见过一些用Perl编写的可怕代码,但我无法理解这一点:

select((select(s),$|=1)[0])
它存在于我们用来与服务器通信的一些网络代码中,我认为这与缓冲有关(因为它设置了
$|


但我不明白为什么会有多个
select
调用或数组引用。有人能帮我吗?

这是一个讨厌的小习惯用法,用于在标准输出以外的文件句柄上设置自动刷新

select()
获取提供的文件句柄并(基本上)用它替换STDOUT,完成后返回旧的文件句柄

因此
(选择($s),$|=1)
重定向文件句柄(记住
select
返回旧的文件句柄),并设置自动刷新(
$|=1
)。它在列表(
(…)[0]
)中执行此操作,并返回第一个值(这是
select
调用的结果-原始STDOUT),然后将其传递回另一个
select
以恢复原始STDOUT文件句柄。呸

但是现在你明白了(好吧,也许;),那么就这样做吧:

use IO::Handle;
$fh->autoflush;

在句柄
s
上打开缓冲区刷新,然后重新选择当前句柄,这是一个过于聪明的代码


有关更多信息,请参见
perldoc-f select

找出任何代码的方法是将其分开。你知道括号内的东西比括号外的东西先发生。这与您了解其他语言中的代码的工作方式相同

第一位是:

( select(s), $|=1 )
该列表有两个元素,它们是两个操作的结果:一个选择
s
文件句柄作为默认值,然后一个将
$|
设置为真值。
$|
是每个文件句柄变量之一,它仅适用于当前选定的文件句柄(请参见“有效文件句柄”)。最后,您有一个包含两项的列表:上一个默认文件句柄(选择的结果)和1

下一部分是用于拉出索引0中的项的文字列表切片:

( PREVIOUS_DEFAULT, 1 )[0]
其结果是单个项是以前的默认文件句柄

下一部分获取切片的结果,并将其用作另一个调用
select

 select( PREVIOUS_DEFAULT );

因此,实际上,您已经在文件句柄上设置了
$|
,并最终返回到使用默认文件句柄开始的位置。

在另一个地方,我曾经提出一个更易于理解的版本,即:

for ( select $fh ) { $| = 1; select $_ }
这保留了紧凑习惯用法的唯一优点,即不需要在周围的范围中声明变量

或者,如果您对
$不满意,可以这样编写:

for my $prevfh ( select $fh ) { $| = 1; select $prevfh }
$prevfh
的范围仅限于
for
块。(但是,如果你编写Perl,你真的没有理由对
$\ucode>感到不安)

请检查。有关
$|
的含义,请检查

选择一个新的默认文件句柄。看

启用自动刷新。看

返回此元组的第一个值

select((select($fh), $|=1)[0])
选择它,即恢复旧的默认文件句柄


相当于

$oldfh = select($fh);
$| = 1;
select($oldfh);
也就是说

use IO::Handle;
$fh->autoflush(1);

如perldoc页面所示。

跳过加载IO::Handle是过度优化

use IO::Handle;
$fh->autoflush(1);

更具可读性。

对不起,我原以为你的意思是“令人讨厌”,因为它的功能不可靠,而不是不够清晰。因此,它将内部选择的结果传递给外部选择,重新选择原始。这是有道理的,但你是对的,我可能会放弃它并使用autoflush。早在IO::Handle->autoflush存在之前,我就编写了这段难看的代码。请让它安静地死去@兰德尔:所以你该受责备!很抱歉,在IO::Handle->autoflush不存在的上下文中,我将其描述为“讨厌的”,那么它确实是一段狡猾的代码;)(/我疯狂地后退)我看不出有任何理由使用Lisp-ish版本。即使在没有IO::Handle的情况下,也可以通过使用
for
主题化旧句柄以进行重新选择,从而更好地编写它。参见我的答案。
perl-wE'openoutput,“>”,undef;假设选择(输出)
不会打印出main::STDOUT。为什么?你和Randall的版本都不那么明显,但至少Randall的版本有短小的优势。。我的$old_fh=select($fh);$|=1.选择($old_fh);不起作用?好像你要追求的是可理解性,这似乎是一个更明智的选择。你有没有看到任何关于它不起作用的说法?在任何情况下,如果您真的想要一个明智的选择,您将使用IO::Handle。至于短小,兰德尔有1-4个字符的优势,取决于空格。顺便说一下,这是一个完全诚实的问题。我从来没有到过必须这样做的地方(至少在IO::Handle不可用的地方)。所以继续使用它吧。我没有声称我的解决方案是最好的,也没有任何其他解决方案不起作用;本着TMTOWTDI的精神,我只是提供了一个Perl-ish而不是Lisp-ish的习语。我更喜欢亚里士多德的版本,因为它不依赖于列表中的执行顺序,而我对列表中的执行顺序表示怀疑(切齿于该方案)。为了迷惑和挫败那些不得不维护代码的人,例如,你。如果有人不想使用
IO::Handle
他们至少可以将这个怪物包装在一个函数中,比如
sub-flush($){select((select($[0]),$[124;=1)[0])}
如果你要将它放在一个函数中,那么你最好用临时的方法编写它,以保持旧的句柄。:)@霍布斯-呃,我可以走任何一条路。对于如此简单的函数,一旦它出现在函数中,您就再也不必查看它了,
flush
函数的功能也不可能改变。也许可怕的版本稍微快一点。也许你可以用它来吓唬实习生。我不会说过度优化。使用词汇
$oldfh = select($fh);
$| = 1;
select($oldfh);
use IO::Handle;
$fh->autoflush(1);
use IO::Handle;
$fh->autoflush(1);