我最近学习了Ruby编程语言,总而言之,它是一门很好的语言.但我很惊讶地发现它并不像我预期的那么简单.更准确地说,"最不惊讶的规则"对我来说似乎并不受到尊重(当然这是非常主观的).例如:
x = true and false
puts x # displays true!
Run Code Online (Sandbox Code Playgroud)
和着名的:
puts "zero is true!" if 0 # zero is true!
Run Code Online (Sandbox Code Playgroud)
你会警告一个Ruby新手的其他"问题"是什么?
max*_*ori 57
来自文章:
$
和@
不指示变量的数据类型和Perl一样,而是充当范围分辨率运营商.99.0
)或显式转换(99.to_f
).附加点(99.
)是不够的,因为数字易受方法语法的影响.0
,""
并且[]
都评价true
.在C中,表达式0 ? 1 : 0
求值为0
(即假).然而,在Ruby中,它会产生1
,因为所有数字都评估为true
; 只nil
和false
评价false
.这个规则的必然结果是按惯例的Ruby方法 - 例如,正则表达式搜索 - 在成功时返回数字,字符串,列表或其他非假值,但是nil
在失败时(例如,不匹配).此约定也用于Smalltalk,其中只有特殊对象true
,false
可以在布尔表达式中使用.char
字符类型的C相比).切片时可能会出现意外:"abc"[0]
yield 97
(一个整数,表示字符串中第一个字符的ASCII码); 获得"a"
使用"abc"[0,1]
(长度为1的子串)或"abc"[0].chr
.statement until expression
与其他语言的等效语句(例如do { statement } while (not(expression));
在C/C++/...中)不同,符号实际上从不运行语句,如果表达式已经存在true
.这是因为statement until expression
实际上是语法糖
until expression
statement
end
Run Code Online (Sandbox Code Playgroud)
,等效其中在C/C++的是while (not(expression)) statement;
就像statement if expression
是一个相当于
if expression
statement
end
Run Code Online (Sandbox Code Playgroud)
但是,符号
begin
statement
end until expression
Run Code Online (Sandbox Code Playgroud)
事实上,即使表达式已经为真,Ruby也会运行一次语句.
Greeting << " world!" if Greeting == "Hello"
不会生成错误或警告.这与final
Java中的变量类似,但Ruby也具有"冻结"对象的功能,这与Java不同.一些与其他语言明显不同的功能:
条件表达式的常用运算符,and
并且or
不遵循正常的优先规则:and
不会比更严格地绑定or
.Ruby也有表达式运算符||
,&&
它可以按预期工作.
def
内部def
不会做Python程序员可能期望的:
def a_method
x = 7
def print_x; puts x end
print_x
end
Run Code Online (Sandbox Code Playgroud)
这给出了关于x
未定义的错误.你需要使用一个Proc
.
语言功能
()
,以避免代码含糊不清.不使用()
仍然是常见的做法,并且使用Ruby作为人类可读的特定于域的编程语言本身以及所谓的方法可能特别好method_missing()
.Min*_*ark 38
新手将遇到平等方法的麻烦:
这些例子应该澄清前3种方法:
a = b = "joe"
a==b # true
a.eql? b # true
a.equal? b # true (a.object_id == b.object_id)
a = "joe"
b = "joe"
a==b # true
a.eql? b # true
a.equal? b # false (a.object_id != b.object_id)
a = 1
b = 1.0
a==b # true
a.eql? b # false (a.class != b.class)
a.equal? b # false
Run Code Online (Sandbox Code Playgroud)
注意==,eql?和平等?应始终是对称的:如果a == b则b == a.
还要注意==和eql?是否在类Object中实现为等于别名?,那么如果你创建一个新类并想要==和eql?除了普通的身份之外,还有其他意义,那么你需要覆盖它们.例如:
class Person
attr_reader name
def == (rhs)
rhs.name == self.name # compare person by their name
end
def eql? (rhs)
self == rhs
end
# never override the equal? method!
end
Run Code Online (Sandbox Code Playgroud)
该===方法的行为不同.首先它是不是对称的(一=== B则不是意味着A在B ===一).正如我所说,你可以将=== b读作"匹配b".这里有一些例子:
# === is usually simply an alias for ==
"joe" === "joe" # true
"joe" === "bob" # false
# but ranges match any value they include
(1..10) === 5 # true
(1..10) === 19 # false
(1..10) === (1..10) # false (the range does not include itself)
# arrays just match equal arrays, but they do not match included values!
[1,2,3] === [1,2,3] # true
[1,2,3] === 2 # false
# classes match their instances and instances of derived classes
String === "joe" # true
String === 1.5 # false (1.5 is not a String)
String === String # false (the String class is not itself a String)
Run Code Online (Sandbox Code Playgroud)
该案件陈述是基于===方法:
case a
when "joe": puts "1"
when 1.0 : puts "2"
when (1..10), (15..20): puts "3"
else puts "4"
end
Run Code Online (Sandbox Code Playgroud)
相当于:
if "joe" === a
puts "1"
elsif 1.0 === a
puts "2"
elsif (1..10) === a || (15..20) === a
puts "3"
else
puts "4"
end
Run Code Online (Sandbox Code Playgroud)
如果你定义一个新类,其实例代表某种容器或范围(如果它有类似于include?或匹配?方法),那么你可能会发现覆盖===方法是有用的,如下所示:
class Subnet
[...]
def include? (ip_address_or_subnet)
[...]
end
def === (rhs)
self.include? rhs
end
end
case destination_ip
when white_listed_subnet: puts "the ip belongs to the white-listed subnet"
when black_listed_subnet: puts "the ip belongs to the black-listed subnet"
[...]
end
Run Code Online (Sandbox Code Playgroud)
Dan*_*ton 20
猴子补丁.Ruby有开放类,所以它们的行为可以在运行时动态改变...
如果或已被覆盖,对象可能会响应未定义的方法.这利用了Ruby的基于消息的方法调用.Rails的ActiveRecord 系统使用它可以产生很好的效果.method_missing
send
Min*_*ark 18
以下代码让我感到惊讶.我认为这是一个危险的问题:既易于碰撞又难以调试.
(1..5).each do |number|
comment = " is even" if number%2==0
puts number.to_s + comment.to_s
end
Run Code Online (Sandbox Code Playgroud)
这打印:
1
2 is even
3
4 is even
5
Run Code Online (Sandbox Code Playgroud)
但是,如果我只是在阻止之前添加comment =
任何东西 ......
comment = nil
(1..5).each do |number|
comment = " is even" if number%2==0
puts number.to_s + comment.to_s
end
Run Code Online (Sandbox Code Playgroud)
然后我得到:
1
2 is even
3 is even
4 is even
5 is even
Run Code Online (Sandbox Code Playgroud)
基本上,当一个变量仅在一个块内定义时,它会在块的末尾被销毁,然后nil
在每次迭代时被重置.这通常是你所期望的.但是如果变量是在块之前定义的,则外部变量在块内使用,因此其值在迭代之间是持久的.
一种解决方案是写这个:
comment = number%2==0 ? " is even" : nil
Run Code Online (Sandbox Code Playgroud)
我认为很多人(包括我)倾向于写" a = b if c
而不是a = (c ? b : nil)
",因为它更具可读性,但显然它有副作用.
Dan*_*aft 16
在super
没有参数的情况下调用时,实际上使用与重写方法相同的参数调用重写的方法.
class A
def hello(name="Dan")
puts "hello #{name}"
end
end
class B < A
def hello(name)
super
end
end
B.new.hello("Bob") #=> "hello Bob"
Run Code Online (Sandbox Code Playgroud)
要实际调用super
没有参数,你需要说super()
.
Min*_*ark 11
我在理解类变量,类属性和类方法时遇到了很多麻烦.这段代码可能对新手有所帮助:
class A
@@classvar = "A1"
@classattr = "A2"
def self.showvars
puts "@@classvar => "+@@classvar
puts "@classattr => "+@classattr
end
end
A.showvars
# displays:
# @@classvar => A1
# @classattr => A2
class B < A
@@classvar = "B1"
@classattr = "B2"
end
B.showvars
# displays:
# @@classvar => B1
# @classattr => B2
A.showvars
# displays:
# @@classvar => B1 #Class variables are shared in a class hierarchy!
# @classattr => A2 #Class attributes are not
Run Code Online (Sandbox Code Playgroud)
块理解起来非常重要,它们随处可见.
方法参数周围不需要括号.您是否使用它们取决于您自己.有人说你应该经常使用它们.
使用raise和rescue进行异常处理,而不是抛出和捕获.
您可以使用,;
但除非您想在一行上放置多个内容,否则您不必使用.
小智 8
我学到的一件事是仔细使用运算符|| =.如果你正在处理布尔值,请特别小心.我通常使用|| = b作为一个捕获全部给'a'一个默认值,如果其他一切都失败了,'a'仍然是零.但如果a为假且b为真,则a将被指定为true.
我在使用包含实例方法和类方法的mixin时遇到了麻烦.这段代码可能对新手有所帮助:
module Displayable
# instance methods here
def display
puts name
self.class.increment_displays
end
def self.included(base)
# This module method will be called automatically
# after this module is included in a class.
# We want to add the class methods to the class.
base.extend Displayable::ClassMethods
end
module ClassMethods
# class methods here
def number_of_displays
@number_of_displays # this is a class attribute
end
def increment_displays
@number_of_displays += 1
end
def init_displays
@number_of_displays = 0
end
# this module method will be called automatically
# after this module is extended by a class.
# We want to perform some initialization on a
# class attribute.
def self.extended(base)
base.init_displays
end
end
end
class Person
include Displayable
def name; @name; end
def initialize(name); @name=name; end
end
puts Person.number_of_displays # => 0
john = Person.new "John"
john.display # => John
puts Person.number_of_displays # => 1
jack = Person.new "Jack"
jack.display # => Jack
puts Person.number_of_displays # => 2
Run Code Online (Sandbox Code Playgroud)
起初,我以为我可以通过简单地执行以下操作来使用包含实例方法和类方法的模块:
module Displayable
def display
puts name
self.class.increment_displays
end
def self.number_of_displays # WRONG!
@number_of_displays
end
[...]
end
Run Code Online (Sandbox Code Playgroud)
不幸的是,方法number_of_displays永远不会被包含或扩展,因为它是"模块类方法".只有"模块实例方法"可以包含在类中(作为实例方法)或扩展到类中(作为类方法).这就是为什么你需要将mixin的实例方法放入一个模块,并将mixin的类方法放入另一个模块中(通常将类方法放入"ClassMethods"子模块中).由于包含了魔术方法,您可以轻松地在一个简单的"包含可显示"调用中包含实例方法和类方法(如上例所示).
这个mixin将按每个类别计算每个显示.计数器是一个类属性,因此每个类都有自己的(如果你从Person类派生一个新类,你的程序可能会失败,因为派生类的@number_of_displays计数器永远不会被初始化).您可能希望用@@ number_of_displays替换@number_of_displays以使其成为全局计数器.在这种情况下,每个类层次结构都有自己的计数器.如果您想要一个全局且唯一的计数器,您应该将它作为模块属性.
当我开始使用Ruby时,所有这些对我来说绝对不是直观的.
我仍然无法弄清楚如何干净地使这些mixin方法中的一些私有或受保护(只有display和number_of_displays方法应作为公共方法包含在内).
(至少,比我最初做的更多关注!)
0..10
(两个点)和0...10
(三个点)之间存在差异.
我非常喜欢Ruby.但这个点点对点点点的东西让我感到困惑.我认为这种微妙的双语法"功能"是:
不应该在我的程序中造成毁灭性的错误.
我认为" and
和" or
是对Perl的点头,这是Ruby更明显的"父母"之一(其中最突出的是Smalltalk).它们都具有低得多的优先级(低于赋值,实际上,这是指出行为的来源)&&
,||
而不是你应该使用的运算符.
其他需要注意的事情并不是很明显:
你并没有真正调用方法/函数,虽然它看起来有点像.相反,如在Smalltalk中,您向对象发送消息.所以method_missing
更像是message_not_understood
.
some_object.do_something(args)
Run Code Online (Sandbox Code Playgroud)
相当于
some_object.send(:do_something, args) # note the :
Run Code Online (Sandbox Code Playgroud)
符号被广泛使用.这是那些开始的东西,:
它们并不是立即显而易见的(好吧,它们不是我的意思)但是你越早掌握它们就越好.
Ruby在"鸭子打字"方面很重要,遵循"如果它像鸭子一样行走并且像鸭子一样呱呱......"这样的主体,它允许用一个共同的方法子集非正式地替换对象而没有任何明确的继承或混合关系.
如果使用attr_writer
或attr_accessor
(或def foo=
)声明一个setter(也就是mutator),请注意从类中调用它.由于变量是隐式声明的,因此解释器总是必须解析foo = bar
为声明一个名为foo的新变量,而不是调用该方法self.foo=(bar)
.
class Thing
attr_accessor :foo
def initialize
@foo = 1 # this sets @foo to 1
self.foo = 2 # this sets @foo to 2
foo = 3 # this does *not* set @foo
end
end
puts Thing.new.foo #=> 2
Run Code Online (Sandbox Code Playgroud)
这也适用于Rails ActiveRecord对象,它们根据数据库中的字段定义访问器.由于它们甚至不是@-style实例变量,因此单独设置这些值的正确方法是使用self.value = 123
或self['value'] = 123
.
方法可以重新定义,并且可能会变得令人费解,直到您找到原因为止。(诚然,当 Ruby on Rails 控制器的操作被错误地重新定义时,此错误可能有点“难以”检测!)
#demo.rb
class Demo
def hello1
p "Hello from first definition"
end
# ...lots of code here...
# and you forget that you have already defined hello1
def hello1
p "Hello from second definition"
end
end
Demo.new.hello1
Run Code Online (Sandbox Code Playgroud)
$ ruby demo.rb
=> "Hello from second definition"
Run Code Online (Sandbox Code Playgroud)
$ ruby -w demo.rb
demo.rb:10: warning: method redefined; discarding old hello1
=> "Hello from second definition"
Run Code Online (Sandbox Code Playgroud)
了解Time和Date类之间的区别.两者都不同,并且在rails中使用它们时产生了问题.Time类有时会与标准ruby/rails库中的其他Time类库冲突.它个人花了我很多时间来了解我的rails应用程序中究竟发生了什么.后来,我想到了
Time.new
它指的是一个我甚至都不知道的位置的图书馆.
对不起,如果我不清楚我想说的话.如果其他人遇到类似问题,请重新解释.