Ruby 一道 ruby 笔试题

hujinpu · 2014年12月22日 · 最后由 rocLv 回复于 2014年12月23日 · 4020 次阅读
class C
  def hello
    @v1 = "var v1 hello"
    puts @v1
    puts @@v1
  end
  @v1 = "var v1"
  @@v1 = "var v1 C"
  puts @v1
end
class D < C
  @@v1 = "var v1 D"
end
C.new.hello
C.new.v1
C.v1

程序执行后,会显示什么?为什么?

这道题最近帮我们 filter 了不少的候选人,同时也说明 ror 虽好,切勿只留于表层。

ps: 我们已经不在大搜车了,目前开始了新的创业。对我们 team 感兴趣的可以过来聊聊,我们持续招聘 ruby geek。联系方式:[email protected]

C.new.v1
C.v1

这个会报错吧,是不是少贴了代码??

#1 楼 @jasonliu 确实会报错,为什么呢?

#2 楼 @hujinpu 找不到 v1 这个方法啊,C 和 C 的父类并没有定义这个方法

进一步具体的问题:

  • 报错的原因是?
  • 为什么显示的是 var v1 D ?
  • 怎么改写程序才能打印 var v1 C ?
  • 怎么改写程序才能打印 var v1 ?

我觉得吧,楼主最好把这道题从头到尾分析一遍,再结合招人的时候应聘者给出的回答,全面的分析一下,写成一篇类似理念输出的文章,告诫更多的人学 ror 不要本末倒置,这样让人回答问题帖子很快就沉了,而且也没有起到应有的告诫新人的作用。

class C
  def hello
    @v1 = "var v1 hello"
    puts @v1
    puts @@v1
  end
  @v1 = "var v1"
  @@v1 = "var v1 C"
  puts @v1
end
class D < C
  @@v1 = "var v1 D"
end
//因为类变量可以被子类使用,所以程序执行到这里的时候@@v1 已经等于"var v1 D"
C.new.hello //所以这里打印的@@v1  "var v1 D"

怎么改写程序才能打印 var v1 C ? 怎么改写程序才能打印 var v1 ?

在哪里打印,调用什么方法打印,可以说的更清楚一点么??

#4 楼 @hujinpu #6 楼 @jasonliu 顺序加载和 ruby geter&seter 方法的问题 @@v1 = "var v1 D" 去掉 打印 var v1 C @v1 = "var v1 hello" 去掉 打印 var v1

#4 楼 @hujinpu 1. 报错的原因:

C.new.v1

没有定义 instance method v1

C.v1

没有定义 class method v1

2. When you set a class variable, you set it for the superclass and all of the subclasses.

3.

class C
  def v1
    "var v1 C"
  end
  def self.v1
    "var v1"
  end
end

$ C.new.v1 => var v1 C

$ C.v1 => var v1

符合要求吗?

#5 楼 @cifery 抱歉,是我的错。 :)

#9 楼 @hujinpu 贵公司招初级 Rails 程序员不?

能答出这道题,ruby 算是达到了多少 level?

11 年教团队成员时,我作的分析:http://ruby-windy.iteye.com/blog/1262640, 现在想想,感慨万份。

我也写过哈,都不记得了(回答不上来有可能是忘了,lz 注意别漏掉人才啊 ……^_^) http://michael-roshen.iteye.com/blog/1580248 http://michael-roshen.iteye.com/blog/1581008

#12 楼 @spacewander 答出这道题不代表什么 level,只代表学习得比较认真。 :)

#11 楼 @springwq 可以发简历过来,这里不是招聘版块,我就不发 jd 了。

ruby 的类变量真是有点邪恶的。

var v1 var v1 hello var v1 C C.new.v1 报错,没有定义 v1 方法 var v1 D

应该输出这样吧,学的不太认真啊~

#19 楼 @zkai 输出: var v1 var v1 hello var v1 D 报错,没有 v1 方法

蛮用心的一道题:

class C
  def hello
    @v1 = "var v1 hello"
    puts @v1
    puts @@v1
  end
  @v1 = "var v1"
  @@v1 = "var v1 C"
  puts @v1
end
class D < C
  @@v1 = "var v1 D"
end
C.new.hello
C.new.v1
C.v1

还有一个疑问,在类中直接给类变量和实例变量赋值,和在 initialzie 中赋值有什么不同? 因为 ruby 中类变量和实例变量都是私有的,但是实例变量可以通过 attr_reader, attr_writer, attr_accessor 定义实例变量的读写权限,所以在上例中假如添加了

attr_reader :v1

C.new.v1 就不会报错,但是输出是 nil

#21 楼 @rocLv def 里面的@v1跟类里面写的@v1不是同一个东西,前者是 C 的实例的实例变量,后者是 C 作为 Class 类实例的实例变量。如果要访问后者,可以C.instance_variable_get :@v1。只要知道在 Ruby 中,类也是一个对象就明白了。

class C
  def hello
    @v1 = "var v1 hello"
    puts @v1  # 2,这里打印出   var v1 hello 
    puts @@v1 # 3 ,这里打印出 var v1 D
  end
  @v1 = "var v1"
  @@v1 = "var v1 C"
  puts @v1   # 1,这里打印出 var v1,这里应该没问题
end
class D < C
  @@v1 = "var v1 D"
end
C.new.hello # 
C.new.v1
C.v1

结果 1 处:应该没有什么问题;

结果 2 处:结果应该也没有什么没问题,但这里要特别注意的是,类的 实例变量 无法在类的 实例方法 中访问,也就是说如果在 C#hello 方法中不定义 @v1 = "var v1 hello" 也无法打印出 var v1 ,再说深一点,类的不同对象所拥有的实例变量也不同,此例 C.new 这个对象所拥有的实例变量只有 hello 方法中的 @v1 ,如果此实例不调用 hello 方法,它连 @1 这个实例变量都没有;

结果 3 处:虽然再 C 类中定义了类变量 @@v1 = "var v1 C" ,但类变量可以在其子类访问修改,在 D 类中会将 其父类 C 的类变量 @@v1 重新定义为 @@v1 = "var v1 D",因为类 D 中的 @@v1 还是属于类 C 的共享类变量,因此再最后调用 puts @@v1 时会打印出 var v1 D ;

最后执行到 C.new.v1 时就会报错,后面 C.v1 也将同样报错。因为也很简单就是没有定义相关的实例方法与类方法。

@piecehealth 我用以下代码试了试:

Class C
  @v1
end

如果按照你说的话

Class.instance_variables

应该输出 [@v1]

不过没有,输出的是 []

#24 楼 @rocLv 猜的:第一没赋值 @v1 = 'xx',第二应该是 C.instance_variables

#24 楼 @rocLv

class C
   @v1 = "ss"
 end

 puts C.instance_variable_get :@v1 => ss

 class D
   @v2
 end

 puts D.instance_variable_get :@v2 => 

#24 楼 @rocLv

class A
  def initialize
    @a
    @b = 1
  end
end

A.new.instance_variables # [:@b]

class B
  @a
  @b = 1
end

B.instance_variables # [:@b]

#20 楼 @realwol 恩,@@v1 应该是 var v1 D

突然就想到了 metaprograming ruby 里的这张纸巾...

这个是 ruby user guide 里面关于 instance variable 的说明:

An instance variable has a name beginning with @, and its scope is confined to whatever object self refers to.

所以我写了如下代码验证:

  class C
    @v1
    puts self

    def initialize
       @v1 = "instance_v1"
       puts self
    end
 end

 C.new

=>
C
#<C:0x21492348>

其实这样更容易理解。 不过对于熟悉几种语言的人来说,这里不能不说是个坑。

还想问下:这样设计的好处在哪里?具体在编程的时候那些情况下可能会用到?

需要 登录 后方可回复, 如果你还没有账号请 注册新账号