【问题标题】:Perl: Length of an anonymous listPerl:匿名列表的长度
【发布时间】:2019-12-11 15:04:52
【问题描述】:

如何获取匿名列表的长度?

perl -E 'say scalar ("a", "b");' # => b

我希望 scalar 在标量上下文中返回列表 - 它的长度。

为什么它返回第二个(最后一个)元素?

它适用于数组:

perl -E 'my @lst = ("a", "b"); say scalar @lst;' # => 2

【问题讨论】:

  • Re "我希望标量返回标量上下文中的列表,它是长度。",标量上下文中的列表计算为其最后一项(也在标量上下文)。换句话说,标量上下文中的a,b,cdo { a; b; c; } 相当。
  • @ikegami,我做了这个例子是为了找出括号何时形成列表以及何时用于排序优先级。但是,从您的评论中,我看到了一些东西。 my $foo = 2 * (3 + 1);。括号是否列出了单个元素 (4) 并在标量上下文中返回其最后一个元素 - 4
  • Parens 不创建列表;他们只是改变优先级。没有列表运算符,也没有在列表上下文中评估任何内容(取决于周围代码的赋值除外)。
  • 我恢复了您对该问题所做的更改,因为您从有关列表长度的问题中删除了该列表。
  • 我认为您可能将“列表”的两个不同定义混为一谈。逗号运算符的一个实例(例如"a", "b",但不是qw( a b ))可以称为列表,并且有人说运算符评估为列表并且子返回列表作为“0或更多标量”的简写(例如,"a", "b" 在您的第二个 sn-p 中计算为一个列表,但不是第一个。)

标签: arrays list scalar perl


【解决方案1】:

你可以使用

my $n = () = f();

适用于您的情况,它是

say scalar( () = ("a", "b") );

say 0+( () = ("a", "b") );

首先,让我们澄清一个误解。

您似乎相信某些运算符会在不考虑上下文的​​情况下评估某种称为列表的数据结构,并且该列表在强制转换为标量上下文时会返回其长度。

所有这些都是不正确的。

运算符必须在标量上下文中精确计算一个标量,并且 sub 必须在标量上下文中精确返回一个标量。在列表上下文中,运算符可以计算任意数量的标量,而 subs 可以返回任意数量的标量。所以当我们说一个操作符对一个列表求值,当我们说一个子返回一个列表时,我们并不是指某个数据结构。我们只是使用“列表”作为“零个或多个标量”的简写。

由于没有列表数据结构之类的东西,它不能被强制转换为标量。上下文不是强制;上下文是操作员首先检查以确定他们评估的内容。他们从字面上让上下文决定他们的行为和他们的回报。由每个操作员决定他们在标量和列表上下文中返回什么,and there's a lot of variance

正如你所指出的,

  • 标量上下文中的@a 运算符计算结果为单个标量:数组的长度。
  • 标量上下文中的逗号运算符计算结果为单个标量:与最后一个操作数的值相同。
  • 标量上下文中的 qw 运算符计算结果为单个标量:它通常会返回的最后一个值。

关于你的问题。

要确定一个运算符在列表上下文中计算时要计算多少个标量,我们需要在列表上下文中计算该运算符。运算符总是在标量上下文中计算为单个标量,因此您尝试强加标量上下文是没有根据的(除非运算符碰巧计算为它在列表上下文中返回的长度,例如 @ 987654328@,但不适用于许多其他运营商)。

解决办法是用

my $n = () = f();

explanation 很复杂。

【讨论】:

  • 那个山羊皮有什么好处? (在这种情况下)。好像没有比perl -le 'sub lcount(@) { @_/1 }; print lcount qw(foo bar baz)'效率高。
  • @mosvy 它避免了编写不必要的子程序。而且从技术上讲,它更高效(更少的操作,并且避免了最昂贵的操作之一,即子调用)。
  • @mosvy "似乎没有" -- 你对它进行了基准测试吗?这将交换一个简单的列表展平(() = f() 由于明显分配给一个空列表而丢弃所有元素)与原型和所有的子调用。更不用说编码简单了?
  • @zdim 我没有进行基准测试,我做了perl -MO=Tersed 它。子调用在哪里?
  • @mosvy lcount 的第二个实例是子调用。
【解决方案2】:

一种方式

perl -wE'$len = () = qw(a b c); say $len'   #--> 3

= () = "operator" 是对context 的一个游戏。它在其右侧强制列表上下文并分配列表的长度。请参阅this post 了解列表与标量分配和this page 了解有关这一切的一些想法。

如果这需要在列表上下文中使用,那么 LHS 上下文也可以由scalar 强制,例如

say scalar( () = qw(a b c) );

或者通过其他方式 (0+...),但 scalar 在这种情况下实际上是合适的,也是最清晰的。


在您的诚实尝试中,scalar 将标量上下文强加在其操作数上——或者这里是一个 表达式,因此由 comma operator 评估,从而一个接一个地被丢​​弃,直到最后一个返回。

您会在打开警告的情况下了解这一点,因为它会发出警告

在 -e 第 1 行的 void 上下文中无用地使用常量(“a”)

警告总是可以在单行中启用,带有-w 标志。我建议这样做。

【讨论】:

  • @ikegami "返回长度的原因..." ---当然,但我发现标量赋值的解释对此并不公平上下文翻转(=()=),其中 RHS 上的列表确实得到了它的“列表处理”,而我们仍然分配标量(当然是 LHS)。不知道怎么表达清楚。
  • @ikegami 是的,这样更好、更简单、更正确。感谢您的编辑:)
  • 感谢您提及-w。我没有注意到它会引发警告。
  • @MiroslavPopov 是的,-w 非常有用。我会说我们希望在单行程序中同样糟糕(就像在生产程序中一样),因为它们主要用于精确地尝试一些事情。对我来说,它掌握在手中——我不认为,我的单行者总是w 标志。
  • 有一种方法可以做到这一点inline
【解决方案3】:

Perl 引用很难。我不会让您阅读prelref,因为它不是任何人都可以阅读并开始使用的。

长话短说,使用此模式获取匿名数组大小:0+@{[ <...your array expression...> ]}

例子:

print 0+@{[ ("a", "b") ]};

【讨论】:

  • 请注意,这不必要地构建了一个数组(这涉及复制每个标量),因此它不仅比前两个答案中的解决方案更复杂(读取),而且速度更慢。
  • "长话短说,使用这种模式来获取匿名数组大小:" --- 简而言之,我说“不要那样做”(除非它是为了玩和混淆),因为它是一种复杂的方式来做一些相当简单的事情。清晰度通常是第一位的,虽然它可能会与效率竞争,但速度也较慢。
猜你喜欢
  • 1970-01-01
  • 2018-01-20
  • 1970-01-01
  • 2011-04-24
  • 2011-08-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-16
相关资源
最近更新 更多