Ruby 有没有必要预先提防新旧版本 ruby 之间可能会有不同的本地变量作用域?

nagae_memooff · 2014年02月08日 · 最后由 ruohanc 回复于 2014年02月10日 · 2572 次阅读

标题提得比较绕,因为确实有点不知道该怎么概括这个问题,见谅呀。 我提的这个问题发自于这样的思考: 弱/动态类型的语言声明变量时,不似传统语言有精确的作用域,一旦写好就不会改变,而是不同程度依赖代码的上下文。我接触过的弱类型语言有 javascript 和 ruby。javascript 中使用 var 来声明临时变量或不使用关键字直接声明外层变量;ruby 不需要使用关键词来声明变量。那么我在实际的编码过程里遇到了类似以下的情况:

一段 if 或者 while 的逻辑,会依据不同结果将相应的不同的值赋给一个或多个临时变量,然后由其他的方法来使用或者返回这个值给客户端。类似下面的逻辑:

if_or_while_some_expressions do |object|
    result = some_functions object
else
    result = another_functions object
end
render_result result

目前我们的这段代码在 ruby1.9.3 中能够正常工作,但我是否需要提防以后版本的 ruby 中会抛出没有 result 这个变量,或者由于作用域的原因而导致 result 的值混成了外层的某个值呢(因为很多其他语言中,if-else 块或者 while 块里的变量只要出了块就会消失,这也符合常识,而 ruby 这种即使出了 if 范围仍然保留本地变量的做法显得不那么大众)。 也就是说,有没有必要写成以下的形式:

result = ""            # 或者写成result=nil或者其他的形式,只要赋个初始值就行
if_or_while_some_expressions do |object|
    result = some_functions object
else
    result = another_functions object
end
render_result result

用以避免可能造成的作用域模糊呢?

  1. Ruby 是强类型、动态语言
  2. 一般这么写
result = if condition
            some_function object
         else
            another_function object
         end
render_result result

活动记录会帮你记住的,没必要。

#1 楼 @Rei 1、我一开始没描述明白,我的意思是弱类型或者动态类型,js 和 ruby 分别是前者和后者,但是例子我只举了 ruby 的,因为 js 那边是临时想到的。 2、如果是一个变量的话这样写很好看,但如果需要多个变量(比如假设一个 code 和一个 data 一个 time)、或者不同条件里所执行的代码并不那么结构一致的话,按您的结构就需要写成

code, data, time = if condition
some_function object
...
# return an array
else
some_other_function object
...
#return another array
end

这样子感觉有点不够整洁。不知是否还有更好的写法呢?

#2 楼 @rasefon “活动记录”是指什么呢?

===== 呃,是指 active_record 吗……?

#4 楼 @nagae_memooff 一个过程的一次执行所需信息的管理,是通过使用一个所谓活动记录的连续存储块来实现的。在 PASCAL 和 C 语言中,我们通常采用以过程为单位的动态存储分配方案。即:当一个过程被调用时,就把它的活动记录推入运行时存储栈的栈顶,而在控制返回调用程序时,再从栈顶弹出相应的活动记录。

另外 ruby 是弱类型的。

#3 楼 @nagae_memooff 有很多方法可以将代码挪来挪去,推荐《重构 Ruby 版》这本书,但是这么一小段代码我怀疑是否有这么大优化价值,不想脱离实际环境讨论 模式 ,除非真的出现问题。

#5 楼 @rasefon 原来如此。了解了。 至于 ruby 我觉得应该属于强弱类型之间,单独用强类型弱类型应该都不能精确描述。如果引入了所谓“动态类型”这个概念,然后把动态类型和弱类型的交集部分刨除掉,就更倾向于强类型吧。比如在数值和文本之间做加法就有严格的抛错,但是可以动态改变类型。 所以我个人倾向于用“ruby 是强类型、但动态类型”的语言描述。

#5 楼 @rasefon

强类型

2.1.0 :001 > 1 + '2'
TypeError: String can't be coerced into Fixnum
        from (irb):1:in `+'
        from (irb):1
        from /home/rei/.rvm/rubies/ruby-2.1.0/bin/irb:11:in `<main>'

弱类型

> 1 + '2'
"12"

@nagae_memooff , 如果你的变量最后要输出为其他 method 的参数,render_result result, 设定默认值是好的做法,还可以节约条件。如果有好几个变量的话,更需要默认值了。你在 3 楼的表述不太清楚,直接拿出需求和原来的代码看能不能改进吧。

#8 楼 @Rei 我觉得至多只能算介于强类型和弱类型之间。比如强类型的变量赋值,ruby 就做不到完全的检查,函数调用也不会,没有静态重载等等。

-0-....这个, 里面提到了作用域的三个域门 (module, class & def), 穿过域门后临时变量才会消失不见,按照这个去理解的话,你就不用担心了。参考

#3 楼 @nagae_memooff 然后你这里提到的方式,都已经开始返回数组了,你是不是应该考虑使用 Struct 或者新建一个类来描述这个复杂的状态才对呢.?

然后,担心这个问题的话,做好测试就行了。目前来说没必要像 js 那样写..

匿名 #13 2014年02月09日

强类型弱类型什么的,没有明确的、广为接受的定义…所以说还是按静态类型和动态类型来说好了么。

至于作用域,应该只是语言设计的问题,其实和类型关系好像不大;

Javascript 的作用域问题则比较特殊了,差不多可算一开始没有想好的吧…所以后来才有了 strict 模式来修正;

Ruby 的 spec 还是很稳定的,在可见的将来,除非这里有什么 bug,否则应该不会再改,就算有情况要改,也还是会在更新文档里说明,到时再看也不迟啊。

PS:Javascript 还有个问题是大部分情况下运行环境多种多样且相对不可控(浏览器),相比起来 Ruby 几乎不会遇上这个问题。

#11 楼 @ruohanc 我目前自己用的是这样的一个方法,就是看上下文里的 self 指的是哪个对象,同一个 self 应该有相同的作用域,是这样的判断方法。不过总感觉是不是有点歪门邪道。 #9 楼 @billy 这帖子只是突然想到这样的问题才来发问的,本身并不是实际中遇到的问题啦。实际中我的写法一般是这样,如果逻辑上某个变量需要默认值,那么先给一个默认值,然后根据判断结果决定是否更改这个默认值;如果不是这种情况,我就又是按其他情况选择不同的写法了。

#13 楼 @dorentus 了解了。

#14 楼 @nagae_memooff

用 self 判断不一定靠谱,还是按照域门判断吧。注意关键词就好了。

另,一般我喜欢用 lazy 方式初始化

Class A
  attr_writer :var_a
  def var_a
    @var_a ||= "default value"
  end
end
需要 登录 后方可回复, 如果你还没有账号请 注册新账号