perl中的上下文

在perl中,很多地方会切换上下文。所谓上下文,它的重点在于同一个表达式出现在不同地方,得到的结果不同。换句话说,同一个表达式,它表达的值不是固定的。这就像是同一个单词,在不同语境下的意思不同。

例如,运算操作符决定数值是一个数字还是一个字符串。

2 * 3
2 x 3

2 * 3中的2和3都是数值,因为操作符*是算术运算符,它要求两边都是数字。而2 x 3中的2是字符串,3是数字,因为操作符x是这样要求的。

还有,对数组@arr的两种操作:

@arr=qw{perl,python,shell};
print @arr,"\n"; # 返回:perlpythonshell
print @arr."\n"; # 返回:3

使用逗号分隔@arr\n是产生一个列表,这时的@arr会替换为该数组中的元素值。使用点号连接@arr\n,这时点号要求两边的都是字符串,数组在这种环境下(标量上下文)返回的是它的元素个数,所以@arr返回一个数值(但其实是字符串)。

在perl解析表达式的时候,你要么希望它返回一个标量,要么希望它返回一个列表(其实还有很多种上下文,但至今无人知晓有多少种上下文,perl长老团也不知道)。所以perl中常见的两种上下文是:标量上下文和列表上下文,除此之外还有一个很常见的上下文类型:空上下文(void context)。

  1. 标量上下文:表达式在标量上下文中返回一个标量
  2. 列表上下文:期待表达式返回一个列表
  3. 空上下文:不使用表达式的返回结果,即返回结果被丢弃

上下文不仅决定了表达式的返回结果类型,还限制了某些环境下只能使用某些上下文。比如,需要传递一个列表的时候却传递了一个标量,这可能会报错误。

例如:

42 + something;     # 这里的something必须是标量
sort something; # 这里的something必须是列表

数组@arr为例:

@arr=qw(perl shell python);
@sorted=sort @arr; # 列表上下文:返回(perl python shell)
@num=@arr + 42; # 标量上下文:返回45

即便是赋值这种操作,都有不同的上下文:

@arr1=@arr;      # 列表上下文,赋值@arr给另一个数组@arr1
$arr1=@arr; # 标量上下文,赋值@arr的元素个数给变量arr1

比较悲剧的是,无法总结一个通用的规则来解释什么时候用什么上下文,只能通过一些经验来感受它。

列表操作切换到标量上下文

那些操作列表的表达式(如sort)用在标量上下文会如何?没人知道会如何。不同的操作,返回的内容没有规律可言。

例如,对列表排序的sort操作放在标量上下文只会返回undef,reverse操作放在标量上下文则是返回字符的逆排序(先将所有元素按照字符串格式连接起来,再对整体进行反转)。

@arr=qw(perl python shell);
$sorted=sort @arr; # 返回undef
$reversed=reverse @arr; # perlpythonshell-->llehsnohtyplrep
print $sorted,"\n";
print $reversed,"\n";

以下是常见的上下文:

$var = something;             # 标量上下文
@arr = something; # 列表上下文
($var1,$var2) = something; # 列表上下文
($var1) = something; # 列表上下文

以下是常见的标量上下文:

$var = something;
$arr[3] = something;
123 + something;
if (something) {...}
wihle(something) {...}
$var[something] = something;

以下是常见的列表上下文:

@arr = something;
($var1,$var2) = something;
($var1) = something;
push @arr,something;
foreach $var (something){...}
sort something;
reverse something;
print something;

需要注意的几点,将数组赋值给标量变量,得到的是数组的长度(元素个数),将列表赋值给标量变量,得到的是最后一个元素,除了最后一个元素外,其它元素都被丢弃,也就是放进了void context。

@arr = qw(a b c d);
$x = @arr; # 结果:$x=4 $y = qw(a b c d); # 结果:$y=d,开启了warnings的情况下会警告
($y) = qw(a b c d); # 结果:$y=d,不会警告
($a,$b,$c,$d) = qw(a b c d) # 结果:$a=a,$b=b,$c=c,$d=d

标量操作切换到列表上下文

这种情况很简单,如果某个操作的返回结果是标量值,但却在列表上下文中,则直接生成一个包含此返回值的列表。

@arr = 6 * 7;    # 结果:@arr=(42)
@arr = "hello".' '.'world'; # 结果:@arr=("hello world")

但关于undef和空列表有一个陷阱:

@arr1 = undef;
@arr2 = ();

上面的undef是一个标量值,所以赋值后@arr1=(undef),它不会清空数组;而()是空列表,它表示未定义的,所以赋值后@arr2被清空。

强制指定标量上下文

有时候如果想要强制指定标量上下文,可以使用伪函数scalar进行强制切换,它会告诉perl这里要切换到标量上下文。

@arr=(perl python shell);
print "How many subject do you learn?\n";
print "I learn ",@arr," subjects!\n"; # 错误,这里会输出课程名称
print "I learn ",scalar @arr," subjects!\n"; # 正确,这里输出课程数量

另一种切换为标量上下文的方式是使用~~,这是两个比特位取反操作,因为比特位操作环境是标量上下文,两次位取反相当于不做任何操作,所以将环境变成了标量上下文。这在写精简程序时可能会用上,而且因为~~是符号,可以和函数之间省略空格分隔符:

my @arr = qw(Shell Perl Python);
print ~~@arr;
print~~@arr;

还可以使用下面的技巧从列表上下文切换成标量上下文:

$var = () = expr

Perl中赋值操作总是先评估右边,所以上面等价于$var = (() = expr)() = expr表示转换成列表上下文,使得expr以列表上下文的环境工作。最后的赋值操作,由于左边是$var,会将列表转换成标量上下文。

(如果不理解,暂时跳过)这种技巧比较常见的是$num = () = <>,因为<>在不同上下文环境下工作的方式是不一样的,这个表达式表示以列表上下文环境读取所有行,然后赋值给标量,所以赋值给标量的是列表的元素个数,也就是文件的行数。

它等价于$num = @tmp = <>。而且完全可以使用scalar()替代scalar(@tmp = <>)

只有强制切换到标量上下文的伪函数scalar,没有切换到列表上下文的函数。因为根本用不到。

列表上下文中的STDIN

<STDIN>放在列表上下文时,会 一次性 读取所有输入(文件/键盘等)。一次性意味着大文件需要大量内存,一般400M的文件,perl可能会花上1G内存,因为perl会事先分配好富裕的空间避免事后问题。

例如:

@lines=<STDIN>;
foreach (@lines){
print $_;
}

在目前来说,这正是我们所需的方式。可以读取每行,并对每行进行操作。但因为是一次性读取,对于大文件来说,这种方法不可取,应该想其它方法。

<STDIN>放在列表上下文时,它会一行一行读取,直到读取到文件结尾(EOF)。但是,如果是读取键盘输入,如何给出EOF?在Linux中,按下CTRL+D(Windows下是CTRL+Z)即可,默认它会发送EOF给操作系统,操作系统会通知perl到了文件结尾。

因为<STDIN>读取的每一行中默认就带有换行符,在列表上下文中,同样可以使用chomp()函数来去除每一行的换行符。

@lines=<STDIN>;
chomp(@lines);

或者采用更简洁的方式:

chomp(@lines=<STDIN>);

Perl中的执行上下文的更多相关文章

  1. 进阶学习js中的执行上下文

    在js中的执行上下文,菜鸟入门基础 这篇文章中我们简单的讲解了js中的上下文,今天我们就更进一步的讲解js中的执行上下文. 1.当遇到变量名和函数名相同的问题. var a = 10; functio ...

  2. js中的执行上下文,菜鸟入门基础。

    console.log(a); //Uncaught ReferenceError: a is not defined 因为没有定义a所以报错了. var a = 52; console.log(a) ...

  3. Javascript本质第二篇:执行上下文

    在上一篇文章<Javascript本质第一篇:核心概念>中,对Javascript执行上下文做了解释,但是这些都是基于Javascript标准中对执行上下文的定义,也就是说理论上的东西,本 ...

  4. 深入理解javascript原型和闭包(9)——简述【执行上下文】下

    继续上一篇文章(http://www.cnblogs.com/wangfupeng1988/p/3986420.html)的内容. 上一篇我们讲到在全局环境下的代码段中,执行上下文环境中有如何数据: ...

  5. JavaScript的执行上下文

    在JavaScript的运行过程中,经常会遇到一些"奇怪"的行为,不理解为什么JavaScript会这么工作. 这时候可能就需要了解一下JavaScript执行过程中的相关内容了. ...

  6. 深入学习JS执行--创建执行上下文(变量对象,作用域链,this)

    一.介绍 本篇继上一篇深入理解js执行--单线程的JS,这次我们来深入了解js执行过程中的执行上下文. 本篇涉及到的名词:预执行,执行上下文,变量对象,活动对象,作用域链,this等 二.预执行 在上 ...

  7. 通俗易懂的来讲讲js的函数执行上下文

    0.开场白 在平时编写JavaScript代码时,我们并不会和执行上下文直接接触,但是想要彻底搞懂JavaScript函数的话,执行上下文是我们绕不过去的一个知识点. 1.执行上下文栈 JavaScr ...

  8. JS进阶之---执行上下文,变量对象,变量提升

    一.结构顺序大体介绍 JavaScript代码的整个执行过程,分为两个阶段,代码编译阶段与代码执行阶段. 编译阶段由编译器完成,将代码翻译成可执行代码,这个阶段作用域规则会确定. 执行阶段由引擎完成, ...

  9. 执行上下文、this

    1.js中的执行上下文或者执行环境:execution context,简称EC; 2. console.log(a);//undefined var a=200; fn('lili'); funct ...

随机推荐

  1. python操作Redis安装、支持存储类型、普通连接、连接池

    一.python操作redis安装和支持存储类型 安装redis模块 pip3 install redis 二.Python操作Redis之普通连接 redis-py提供两个类Redis和Strict ...

  2. dhcp服务简单配置

    dhcp服务搭建 注意事项: > 配置虚拟机虚拟网络编辑器,取消"使用本地DHCP服务将IP地址分配给虚拟机" > 虚拟机网络连接设置为"仅主机模式" ...

  3. 【hdu2000】ASCII码排序

    题目来源:www.acm.hdu.edu.cn 题目编号:2000 ASCII码排序 /*----------------------------------------原题目------------ ...

  4. 《JavaScript 高级程序设计》读书笔记二 使用JavaScript

    一   <script>元素 a.四个属性: async:立即异步加载外部脚本: defer:延迟到文档完全被解析再加载外部脚本: src:外部脚本路径: type:脚本语言的内容类型: ...

  5. ETC(电子不停车收费系统)的发展演变

    ETC引进中国是在上世纪的90年代中期,当时中国部分经济发达地区的高速公路车流量激增,从而导致了收费口的交通堵塞.高速公路堵车现象时有发生,拥堵严重的路段可能会天天堵,有时候一堵好几天.高速公路管理手 ...

  6. 一次 Java 内存泄漏的排查

    由来 前些日子小组内安排值班,轮流看顾我们的服务,主要做一些报警邮件处理.Bug 排查.运营 issue 处理的事.工作日还好,无论干什么都要上班的,若是轮到周末,那这一天算是毁了. 不知道是公司网络 ...

  7. 设置HttponlyCookie解决mshtml编程无法获取验证码图片流

    最近给客户做的项目有一个新需求,客户需要在打开的IE浏览器中做自动登录,登录的页面上有神兽验证码.解决验证码的方案是找第三方平台打码.这样就有一个问题,如何把正确的验证码传给第三方打码平台. 大家都知 ...

  8. JAVAEE企业级应用开发浅谈之MVC 中的V-VIEW视图

    Step1.情景概要 Hello,小伙伴们,好久不见,之前跟大家分享了三层架构与MVC思想,相信大家对于这两块内容有了相对清晰的个人认识了,既然我们讲到了MVC,这里我们接着这块内容继续往下深入,今天 ...

  9. Javascript高级编程学习笔记(39)—— DOM(5)Comment

    Comment类型 顾名思义,comment类型指的就是注释节点在HTML文档中的类型 也就是说平时我们在代码中的注释,在HTML解析的时候也会被解析为一类节点 让我们可以根据这些节点进行一系列的操作 ...

  10. 【翻译】Neural Collaborative Filtering--神经协同过滤

    [说明] 本文翻译自新加坡国立大学何向南博士 et al.发布在<World Wide Web>(2017)上的一篇论文<Neural Collaborative Filtering ...