Summary of syntax and language features of ruby

Time:2021-11-22

Ruby is an interpreted, object-oriented, dynamically typed language. Ruby’s strategy is to find a balance between flexibility and runtime security. With the emergence of rails framework, ruby became a blockbuster around 2006, and also guided people to regain the fun of programming. Although ruby is not very efficient in terms of execution speed, it can greatly improve the programming efficiency of programmers. This article describes the basic language features of the ruby language, including the basic syntax and the definition of code blocks and classes.

1. Foundation
In the ruby interactive command line, enter the following command (> > is the command line prompt and = > is the return value; the = > symbol and statement will be written in one line to indicate its return value):

?
1
2
3
4
5
6
7
8
9
10
>> puts 'hello, world'
hello, world
=> nil
 
>> language = 'Ruby'
=> "Ruby"
 
>> puts "hello, #{language}"
hello, Ruby
=> nil

The above code uses puts output, assigns values to variables, and implements string replacement with #{}’s syntax. This shows that ruby is interpreted and executed; Variables can be initialized and assigned directly without declaration; Each Ruby code will return a value; A string contained in single quotation marks means that it will be interpreted directly, and a string contained in double quotation marks will cause string substitution.

1.1 programming model

Ruby is a pure object-oriented language. Everything in ruby is an object. You can use “.” to call the methods of the object. You can view the object type and supported methods through class and methods methods, such as 4. Class = > fixnum, 7. Methods = > [“inspect”, “%”, “< <,” numerator “,…], false. Class = > false class (square brackets indicate array).

1.2 process control

Conditional judgment has normal block form and simple single line form; In addition to the common if statements, there are unless statements (equivalent to if not, but more readable). Similarly, loops have normal block form and single line form. Note: except nil and false, other values represent true, including 0!

?
1
2
3
4
5
6
7
8
#Block form
if x == 4
 puts 'This is 4.'
end
#Single line form
puts 'This is false.' unless true
x = x + 1 while x < 10 #The result of X is 10
x = x - 1 until x == 1 #The result of X is 1

Similar to other C family languages, Ruby’s logical operators and (& &) and or (|) have their own short circuit function. If you want to execute the whole expression, you can use & or|

1.3 duck type

Executing 4 + ‘four’ will result in a typeerror error, indicating that ruby is a strongly typed language. In case of type conflict, you will get an error. If a statement is placed in the def… End function definition, an error will be reported only when calling the function, indicating that Ruby performs type checking at runtime rather than at compile time, which is called dynamic type. Ruby’s type system has its own potential advantage, that is, multiple classes can be used in a “polymorphic” way without inheriting from the same parent class:

?
1
2
3
a = ['100', 100.0]
puts a[0].to_i # => 100
puts a[1].to_i # => 100

This is the so-called “duck type”. The first element of the array is of type string, and the second element is of type float, but the conversion to an integer is to_ i。 Duck type doesn’t care what its internal type is. As long as an object walks like a duck and quacks like a duck, it is a duck. In the idea of object-oriented design, there is an important principle: code the interface, not the implementation. With duck type, this principle can be easily implemented with little extra work.

1.4 function

?
1
2
3
def tell_the_truth
 true
end

Each function will return the result. If the return value is not explicitly specified, the function will return the value of the last processed expression before exiting the function. A function is also an object and can be passed to other functions as parameters.

1.5 array

Like python, ruby arrays are also defined by brackets, such as animals = [‘lion ‘,’ Tiger ‘,’ bear ‘]; Negative subscripts can return the reciprocal elements, such as animals [- 1] = > “bear”; Obtain the elements of a section by specifying a range object, such as animals [1.. 2] = > [‘tiger ‘,’ bear ‘]. In addition, array elements can be different from each other. Most of them are arrays, and they are just arrays of arrays. Array has a very rich API, which can be used to implement queues, linked lists, stacks, collections and so on.

1.6 hash table

?
1
2
3
numbers = {2 => 'two', 5 => 'five'}
stuff = {:array => [1, 2, 3], :string => 'Hi, mom!'}
# stuff[:string] => "Hi, mom!"

The hash table can take any type of key, and the key of the above code’s stuff is special — it is a symbol preceded by a colon identifier. Symbols are very useful when naming things and concepts. For example, two strings with the same value are physically different, but the same symbol is the same physical object. You can call ‘I am string’. Object repeatedly_ ID and: symbol.object_ ID to observe. In addition, when the hash table is used as the last parameter of the function, braces are optional, such as tell_ the_ truth :profession => :lawyer。

2. Object oriented
2.1 code block

Code blocks are unnamed functions (anonymous functions) that can be passed as parameters to functions. When the code block occupies only one line, it is wrapped in curly braces. When it occupies multiple lines, it is wrapped in do / end, which can take several parameters.

?
1
2
3.times {puts 'hehe'} #Output 3 lines hehe
['lion', 'tiger', 'bear'].each {|animal| puts animal} #Output the contents of the list

The above times is actually a fixnum type method. It is very easy to implement such a method by yourself:

?
1
2
3
4
5
6
7
8
9
class Fixnum
 def my_times
  i = self
   while i > 0
    i = i - 1
    yield
  end
 end
end
?
1
3.my_times {puts 'hehe'} #Output 3 lines hehe

This code opens an existing class and adds a custom my to it_ Times method and call the code block with yield. In ruby, code blocks can be used not only for loops, but also for delaying execution, that is, the behavior in the code block will not be executed until the relevant yield is called. Code blocks are filled with various libraries of ruby, ranging from every line of a file to various complex operations on a collection, which are completed by code blocks.

Type 2.2

Call the class method of an object to view its type, and call superclass to view the parent class of this type. The following figure shows the inheritance chain of numbers. The horizontal arrow indicates that the instantiated object on the left is on the right, and the vertical arrow indicates that the lower edge inherits from the upper edge. Everything in ruby has a common ancestor object.

Summary of syntax and language features of ruby

Finally, through a complete example – defining a tree, let’s see how Ruby classes are defined and used. The points needing attention are written in the comments.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Tree
 #Define instance variables, using attr or attr_ Accessor keyword. The former defines a variable and a getter method with the same name as the access variable (i.e. read-only), and the latter defines more variables with the same name as the setter method (note that the symbol is used here)
 attr_accessor :children, :node_name
 
 #Constructor (constructor must be named initialize)
 def initialize(name, children=[])
  @node_name = name
  @children = children
 end
 
 #Traverse all nodes and execute the code block. Note that adding a & before the parameter means that the code block is passed to the function as a closure
 def visit_all(&block)
  visit &block
  children.each {|c| c.visit_all &block}
 end
 
 #Access a node and execute a code block
 def visit(&block)
  block.call self
 end
end
 
ruby_tree = Tree.new("Ruby",
 [Tree.new("Reia"),
  Tree.new("MacRuby")])
#Access a node
ruby_tree.visit {|node| puts node.node_name}
#Access the entire tree
ruby_tree.visit_all {|node| puts "Node: #{node.node_name}"}

Let’s mention the naming convention of Ruby:

(1) Class is named by camelCase
(2) The instance variable (an object has a value) must be preceded by @, and the class variable (a class has a value) must be preceded by @@@
(3) Variable and method names are all lowercase, and underline naming method is used, such as underscore_ style
(4) Constants are named with all uppercase underscores, such as all_ CAPS_ STYLE
(5) Functions and methods used for logic testing generally need to add a question mark, such as if test?

3. Module and mixing
Object oriented language uses inheritance to propagate behavior to similar objects. If an object inherits multiple behaviors, one way is to use multiple inheritance, such as C + +; Java uses the interface to solve this problem, and Ruby uses the module mixin. A module is a collection of functions and constants. If a module is included in a class, its behavior and constants will also become part of the class.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#Define module tofile
module ToFile
 #Get file name
 def filename
  "object_name.txt"
 end
 
 #Create file
 def to_f
  File.open(filename, 'w') {|f| f.write(to_s)} #Notice here to_ S is defined elsewhere!
 end
end
 
#Define user classes
class Person
 include ToFile
 attr_accessor :name
 
 def initialize(name)
  @name = name
 end
 
 def to_s
  name
 end
end
 
Person.new('matz').to_f #Created a file object_ Name.txt, which contains Matz

The above code is easy to understand, but one thing to note: to_ S is used in modules and implemented in classes, but when defining a module, the class that implements it has not even been defined. This is the essence of the duck type. The ability to write files has nothing to do with the person class (a class should do its own thing), but the actual development needs the additional function of writing the person class to files. At this time, mixin can easily meet this requirement.

Ruby has two important mixins: enumerable and comparable. If you want the class to be enumerable, you must implement each method; To make a class comparable, you must implement the < = > (spacecraft) operator (compare the operands A and B, and return 1, 0, or – 1). Ruby strings can be compared as follows: ‘begin’ < = > ‘end = > – 1. Arrays have many useful methods:

?
1
2
3
4
5
6
7
8
a = [5, 3, 4, 1]
a.sort => [1, 3, 4, 5] #Integers have implemented the spacecraft operator through the fixnum class, so they are comparable and sortable
a.any? {|i| i > 4} => true
a.all? {|i| i > 0} => true
a.collect {|i| i * 2} => [10, 6, 8, 2]
a.select {|i| i % 2 == 0} => [4]
a.member?(2) => false
a.inject {|product, i| product * i} => 60 #The first parameter is the result of the last execution of the code block. If the initial value is not set, the first value in the list is used as the initial value

4. Metaprogramming
The so-called meta programming, to put it bluntly, is “writing a program that can write a program”, which is a bit awkward. The following will be explained by examples.

4.1 open class

You can redefine any class in ruby and extend them to any method you want, or even completely paralyze ruby, such as redefining the class. New method. For development classes, this trade-off mainly considers freedom. With the freedom to redefine any class or object, you can write code that is easy to understand. However, you should also understand that the greater the freedom and ability, the heavier the responsibility.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Numeric
 def inches
  self
 end
 def feet
  self * 12.inches
 end
 def miles
  self * 5280.feet
 end
 def back
  self * -1
 end
 def forward
  self
 end
end

By opening the numeric class, the above code can use the simplest syntax to express the distance in inches: puts 10.miles.back, puts 2.feet.forward.

4.2 使用method_missing

Ruby找不到某个方法时,会调用一个特殊的回调方法method_missing显示诊断信息。通过覆盖这个特殊方法,可以实现一些非常有趣且强大的功能。下面这个示例展示了如何用简洁的语法来实现罗马数字。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Roman
 # 覆盖self.method_missing方法
 def self.method_missing name, *args
  roman = name.to_s
  roman.gsub!("IV", "IIII")
  roman.gsub!("IX", "VIIII")
  roman.gsub!("XL", "XXXX")
  roman.gsub!("XC", "LXXXX")
 
  (roman.count("I") +
   roman.count("V") * 5 +
   roman.count("X") * 10 +
   roman.count("L") * 50 +
   roman.count("C") * 100)
 end
end
 
puts Roman.III # => 3
puts Roman.XII # => 12

我们没有给Roman类定义什么实际的方法,但已经可以Roman类来表示任何罗马数字!其原理就是在没有找到定义方法时,把方法名称和参数传给method_missing执行。首先调用to_s把方法名转为字符串,然后将罗马数字“左减”特殊形式转换为“右加”形式(更容易计数),最后统计各个符号的个数和加权。

当然,如此强有力的工具也有其代价:类调试起来会更加困难,因为Ruby再也不会告诉你找不到某个方法。因此method_missing是一把双刃剑,它确实可以让语法大大简化,但是要以人为地加强程序的健壮性为前提。

4.3 使用模块

Ruby最流行的元编程方式,非模块莫属。下面的代码讲述如何用模块的方式扩展一个可以读取csv文件的类。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
module ActsAsCsv
 
 # 只要某个模块被另一模块include,就会调用被include模块的included方法
 def self.included(base)
  base.extend ClassMethods
 end
 
 module ClassMethods
  def acts_as_csv
   include InstanceMethods
  end
 end
 
 module InstanceMethods
  attr_accessor :headers, :csv_contents
 
  def initialize
   read
  end
 
  def read
   @csv_contents = []
   filename = self.class.to_s.downcase + '.txt'
   file = File.new(filename)
   @headers = file.gets.chomp.split(', ') # String的chomp方法去除字符串末尾的回车换行符
   file.each do |row|
    @csv_contents << row.chomp.split(', ')
   end
  end
 end
 
end # end of module ActsAsCsv
 
class RubyCsv  # 没有继承,可以自由添加
 include ActsAsCsv
 acts_as_csv
end
 
m = RubyCsv.new
puts m.headers.inspect
puts m.csv_contents.inspect

上述代码中RubyCsv包含了ActsAsCsv,所以ActsAsCsv的included方法中,base就指RubyCsv,ActsAsCsv模块给RubyCsv类添加了唯一一个类方法acts_as_csv,这个方法又打开RubyCsv类,并在类中包含了所有实例方法。如此这般,就写了一个会写程序的程序(通过模块来动态添加类方法)。

一些出色的Ruby框架,如Builder和ActiveRecord,都会为了改善可读性而特别依赖元编程。借助元编程的威力,可以做到尽量缩短正确的Ruby语法与日常用于之间的距离。注意一切都是为了提升代码可读性而服务。

5. 总结
Ruby的纯面向对象可以让你用一致的方式来处理对象。鸭子类型根据对象可提供的方法,而不是对象的继承层次,实现了更切合实际的多态设计。Ruby的模块和开放类,使程序员能把行为紧密结合到语法上,大大超越了类中定义的传统方法和实例变量。
核心优势:
(1)优雅的语法和强大的灵活性
(2)脚本:Ruby是一门梦幻般的脚本语言,可以出色地完成许多任务。Ruby许多语法糖可以大幅提高生产效率,各种各样的库和gem(Ruby包)可以满足绝大多数日常需要。
(3)Web开发:很多人学Ruby最终就是为了用Ruby on Rails框架来进行Web开发。作为一个极其成功的MVC框架,其有着广泛的社区支持及优雅的语法。Twitter最初就是用Ruby实现的,借助Ruby无比强大的生产力,可以快速地开发出一个可推向市场的合格产品。
不足之处:
(1)性能:这是Ruby的最大弱点。随着时代的发展,Ruby的速度确实是越来越快。当然,Ruby是创建目的为了改善程序员的体验,在对性能要求不高的应用场景下,性能换来生产效率的大幅提升无疑是值得的。
(2)并发和面向对象编程:面向对象是建立在状态包装一系列行为的基础上,但通常状态是会改变的。程序中存在并发时,这种编程策略就会引发严重问题。
(3)类型安全:静态类型可提供一整套工具,可以更轻松地构造语法树,也因此能实现各种IDE。对Ruby这种动态类型语言来说,实现IDE就困难得多。