`
RednaxelaFX
  • 浏览: 3009380 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

关于||=与变量定义

    博客分类:
  • Ruby
阅读更多
在Ruby里,给可能未定义或值为nil的变量设置一个默认初始值时,惯用法是使用||=的办法,例如:
a ||= []
a << 'first'

这个例子里,无论a原本有没有定义,或者定义是什么都好,都会被当作是一个变量。如果原本a是一个有非nil非false值的变量,那么a的值不变;如果a原本是一个方法,那么作为方法名的a会被作为变量名的a所屏蔽,并且a被赋值为[]。
看下面这个例子:
a ||= 1
puts a               # 1

def b
  2
end

puts b               # 2
puts b || 3          # 2

b ||= 3
puts b               # 3
puts method(:b).call # 2

可以看到变量名屏蔽方法名的行为。

OK,那有什么好说的呢?Ola Bini最近做过一个关于在JVM上创造自己的语言的讲演,其中有一部分是向与会者介绍Ruby。他演示的时候提到诸如a ||= 1没问题,但他一时以为在a还没定义的时候a = a || 1不行,在演示的时候稍微卡了一下。乍一看,要是根据他当时的思路,确实会觉得这个不行。
思路是:如果单独写a || 1,在a还没定义的时候,访问a的值会得到“未定义局部变量或方法”的错误;把这个放在赋值的右手边,就应该会在能够赋值之前就先遇到错误了。

但实际上,a ||= 1就是a = a || 1的简写,这两者在运行时的行为是一样的。那为什么单独写a || 1会出问题,而放在赋值的右手边就没事了呢?
因为Ruby要想办法知道一个名字到底是变量还是方法调用。最直观的判断依据是,执行到当前位置时这个名字有没有出现在赋值符号的左手边,有的话就把这个名字当作变量,没有的话就默认把这个名字当作方法;后一种情况中如果没有这个名字的方法,那就出错了。
像这样就会出错:
def foo
  puts b  # undefined local variable or method `b'
  b = 1 if false
end

foo

而这样就不会:
def foo
  b = 1 if false
  puts b  # nil
end

foo

也就是说不用真的执行赋值,只要Ruby解析器能看到某个名字出现在赋值符号的左边就够了,就会创建一个这个名字的变量,未赋值时值为nil。

回到a = a || 1。这里Ruby先看到了a出现在赋值符号的左手边,于是创建出变量a,值为nil;接下来对赋值符号右手边求值,nil || 1的结果是1;然后完成赋值,a的值就是1了。单独的a || 1则没这待遇,得看前面有没有别的地方让a存在,不然就会出错。

以前在好多地方都有人提到过Ruby的这个行为。昨晚跟NS老兄聊的时候突然想起||的问题一时没想通,多得他提醒才又想起来了。记下来免得以后又糊涂……
分享到:
评论
2 楼 RednaxelaFX 2009-04-07  
night_stalker 写道
油头粉面的 Ola 被卡住的时候真尴尬……

嗯 XDD
顺带一提,那段录像可以在Ola Bini的blog上找到链接,在这里
对应的幻灯片在这里
1 楼 night_stalker 2009-04-05  
油头粉面的 Ola 被卡住的时候真尴尬……

其实这个特性有好处的:如果不小心写错了一个字母,就会弹出一个error

def fun very_long_parameter_nameeeeeeeeeeeee
  very_long_parameter_nameeeeeeeeeeee || 'bastard!'
end

相关推荐

    3_RH850F1L_定义一个变量到指定地址.7z

    RH850/F1L是瑞萨的一款汽车级的32bit芯片/MCU,应用中经常遇到很多把某个变量定义到指定地址的要求,这份资料就是说明如何定义一个变量到指定地址的,使用的IDE是CS+。 适用于使用RH850/F1L芯片进行软件开发的...

    Oracle定义PLSQL变量学习

    对于Oracle学习者用处很大,是关于Oracle变量及在存储过程、触发器及函数的使用

    关于function类中定义变量this的简单说明

    关于function类中定义变量this的简单说明 &lt;!DOCTYPE html&gt; &lt;html&gt; &lt;head&gt; &lt;/head&gt; [removed] function TObject(){ this.name1 = "aa";//这里不能写name,name是window的变量。否则无法...

    Python面向对象之类和对象实例详解

    #实例变量:定义在方法中的变量,只作用于当前实例的类。 例子: class Turtle:#python 中类名约定以大写字母开头 '''关于类的简单例子。。。''' #属性 == 类变量 color ="green" weight="10kg" legs=4 shell...

    Vue中定义全局变量与常量的各种方式详解

    本文主要跟大家介绍了关于Vue定义全局变量与常量的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: 我想要定义一个变量, 在项目的任何地方都可以访问到, 不需要每一次使用的时候, 都引入...

    gRPC 关于c#的demo,如何定义变量,以及接口服务用法

    gRPC 关于c#的demo,内容包含proto文件变量以及方法接口的定义,以及接口功能的c#实现

    微信小程序关于变量对象data 和 前端wxml取后台js变量值

    第一,前端wxml的数据渲染是通过设置此对象中定义的变量进行关联展现的  第二,定义JS页面中的页面局部变量,使其整个页面中可使用或调用 对象data定义的变量支持各种数据类型,string,int,[],{} **第一.**wxml数据...

    关于CString 变量的错误

    文件如下 VC2005

    关于全局变量和局部变量的那些事

    变量对于学习js,学习编程语言的同学在熟悉不过了,在这里就不在阐述官方的定义了,网上太多了,今天我们就从生活中来理解他 1.什么是变量? 比如: 一个水杯里面装了水,这个水杯就是变量; 一瓶啤酒,这个啤酒瓶...

    关于C中函数声明与定义

    总结如下:1.一般情况下,函数在调用子函数时,子函数必须先声明,要不会报错。(一般都将函数的声明放在一下头文件里)2.如若子函数为返回值是int时,...3.static修饰的函数作用域为从声明/定义处到源文件结尾处为止。

    js类中的公有变量和私有变量

    在cnblogs上看了关于js的一些文章 ,做下笔记: 先看代码1: function car(){ var wheel = 3;//私有变量 this.wheel = 4;//公有变量 alert&#40;wheel&#41;; alert&#40;this.wheel&#41;; } var car1 = new...

    ES6 let和const定义变量与常量的应用实例分析

    本文实例讲述了ES6 let和const定义变量与常量的应用。分享给大家供大家参考,具体如下: 关于 let let是小作用域的变量的声明 { var a = 12; let b = 15; { console.log(a); // 12 console.log(b); // 15 } ...

    keil c51中变量 常量 函数的定位

    关于在keil c51中如何定位变量、常数和函数的位置

    PID算法的运用

    //定义一个结构体,这个结构体用来存算法中要用到的各种数据 bit g_bPIDRunFlag = 0; //PID运行标志位,PID算法不是一直在运算。而是每隔一定时间,算一次。 /* *************************************************...

    C/C++语言中全局变量重复定义问题的解决方法

    主要给大家介绍了关于C/C++语言中全局变量重复定义问题的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。

    c++基础教程,简洁扼要

    union的概念与struct和class定义的类不同, 因为union在同一时间只能存储一个数据成员。但是由union定义的类也是可以有成员函 数的。union定义的类访问权限默认为public。 94 操作符重载(Overloading operators) ...

    php 变量引用与变量销毁机制详细介绍

    //定义一个变量,下面赋值给$b $b = $a;//这一步没有在$a之前加符号&,像这样子”$b= & $a”。没有加&,实际上原理是会将变量$a复制拷贝一份,也就是内存中重新申请一个地址存储变量$b了 ps:在php中,使用”=”直接...

    Python全局变量与局部变量区别及用法分析

    本文实例讲述了Python全局变量与局部变量区别及用法。分享给大家供大家参考,具体如下: 对于很多初学的同学,对全局和局部变量容易混淆,看看下面给大家的讲解相信都应该明白两者的区别了。 定义: 全局变量:在...

    windows用户称拦截api

    对于参数,可以先定义一个参数变量,然后取变量地址就ok了。 如果是想拦截其他进程中的api,则必须使用其他一些方法,最典型的方法是利用VirtualAllocEx函数来在其他进程中申请和提交内存空间。然后用...

Global site tag (gtag.js) - Google Analytics