Seven languages in seven weeks (notes on Ruby)

本文深入介绍了Ruby语言的各种特性,包括语法糖、纯面向对象、动态类型等,并通过实例展示了Ruby的强大功能,如元编程、模块混入及常用集合操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Ruby


Syntactic sugar: describes a language feature that makes code easier to read and write, though there are alternative ways to express the same code.

Ruby’s creator, doesn't worry about the efficiency of the language. He optimizes the efficiency of the programmers.


Ruby is an interpreted, object-oriented, dynamically typed language from a family of so-called scripting languages.

Object-oriented means the language supports encapsulation (data and behavior are packaged together), inheritance through classes (object types are organized in a class tree), and polymorphism (objects can take many forms).


use ruby with console (irb):

>> puts 'hello, world'
hello, world
=> nil
>> language = 'Ruby'
=> "Ruby"
>> puts "hello, #{language}"
hello, Ruby
=> nil
>> language = 'my Ruby'
=> "my Ruby"
>> puts "hello, #{language}"
hello, my Ruby
=> nil

two types of strings: One quote around a string means the string should be interpreted literally, and two quotes leads to string evaluation.

Ruby is a pure object-oriented language:

>> 4
=> 4
>> 4.class
=> Fixnum
>> 4 + 4
=> 8
>> 4.methods
=> ["inspect", "%", "<<", "singleton_method_added", "numerator", ...
" * ", "+", "to_i", "methods", ... ]

if, unless:

>> x = 4
=> 4
>> puts 'This appears to be false.' unless x == 4
=> nil
>> puts 'This appears to be true.' if x == 4
This appears to be true.
=> nil
>> if x == 4
>>   puts 'This appears to be true.'
>> end
This appears to be true.
=> nil
>> unless x == 4
>>   puts 'This appears to be false.'
>> else
?>   puts 'This appears to be true.'
>> end
This appears to be true.
=> nil
>> puts 'This appears to be true.' if not true
=> nil
>> puts 'This appears to be true.' if !true
=> nil

while, until:

>> x = x + 1 while x < 10
=> nil
>> x
=> 10
>> x = x - 1 until x == 1
=> nil
>> x
=> 1
>> while x < 10
>> x = x + 1
>> puts x
>> end

everything but nil and false evaluate to true . C and C++ programmers, take note. 0 is true !

irb(main):023:0> 5.class
=> Fixnum
irb(main):024:0> 5.0.class
=> Float
irb(main):025:0> 5 == 5.0
=> true

Ruby is strongly typed, meaning you’ll get an error when types collide. Ruby makes these type checks at run time, not compile time.

The keyword def defines a function but doesn't execute it.

converts to integer, method 'to_i':

>> i = 0
=> 0
>> a = ['100', 100.0]
=> ['100', 100.0]
>> while i < 2
>>   puts a[i].to_i
>>   i = i + 1
>> end
100
100

Duck Typing:  http://en.wikipedia.org/wiki/Duck_typing

Duck typing doesn’t care what the underlying type might be. If it walks like a duck and quacks like a duck, it’s a duck. In this case, the quack method is to_i .


Every function returns something. If you do not specify an explicit return, the function will return the value of the last expression that’s processed before exiting. Like everything else, this function is an object.

>> def tell_the_truth
>> true
>> end

array:

>> animals = ['lions', 'tigers', 'bears']
=> ["lions", "tigers", "bears"]
>> puts animals
lions
tigers
bears
=> nil
>> animals[0]
=> "lions"
>> animals[2]
=> "bears"
>> animals[10]
=> nil
>> animals[-1]
=> "bears"
>> animals[-2]
=> "tigers"
>> animals[0..1]
=> ['lions', 'tigers']
>> (0..1).class
=> Range

>> a[0] = 0
NameError: undefined local variable or method `a' for main:Object
from (irb):23
>> a = []
=> []
>> [1].class
=> Array
>> [1].methods.include?('[]')
=> true
>> # use [1].methods.include?(:[]) on ruby 1.9
>> a[0] = 'zero'
=> "zero"
>> a[1] = 1
=> 1
>> a[2] = ['two', 'things']
=> ["two", "things"]
>> a
=> ["zero", 1, ["two", "things"]]
>> a = [[1, 2, 3], [10, 20, 30], [40, 50, 60]]
=> [[1, 2, 3], [10, 20, 30], [40, 50, 60]]
>> a[0][0]
=> 1
>> a[1][2]
=> 30
>> a = [1]
=> [1]
>> a.push(1)
=> [1, 1]
>> a = [1]
=> [1]
>> a.push(2)
=> [1, 2]
>> a.pop
=> 2
>> a.pop
=> 1

Arrays have an incredibly rich API. You can use an array as a queue, a linked list, a stack, or a set.


hash:

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

The last hash: A symbol is an identifier preceded with a colon, like :symbol . Symbols are great for naming things or ideas. Although two strings with the same value can be different physical strings, identical symbols are the same physical object. You can tell by getting the unique object identifier of the symbol several times.

Ruby does not support named parameters:

>> def tell_the_truth(options={})
>>   if options[:profession] == :lawyer
>>     'it could be believed that this is almost certainly not false.'
>>   else
>>     true
>>   end
>> end
=> nil
>> tell_the_truth
=> true
>> tell_the_truth :profession => :lawyer
=> "it could be believed that this is almost certainly not false."

Notice that you didn’t have to type in the braces. These braces are optional for the last parameter of a function.


Code Blocks:

A code block is a function without a name. You can pass it as a parameter to a function or a method.

>> 3.times {puts 'hiya there, kiddo'}
hiya there, kiddo
hiya there, kiddo
hiya there, kiddo

The code between braces is called a code block. times is a method on
Fixnum that simply does something some number of times, where some-
thing is a code block and number is the value of the Fixnum . You can
specify code blocks with {/} or do/end . The typical Ruby convention is
to use braces when your code block is on one line and use the do/end
form when the code blocks span more than one line. Code blocks can
take one or more parameters

>> animals = ['lions and ', 'tigers and', 'bears', 'oh my']
=> ["lions and ", "tigers and", "bears", "oh my"]
>> animals.each {|a| puts a}
lions and
tigers and
bears
oh my

custom implementation of a method:

>> class Fixnum
>>   def my_times
>>     i = self
>>     while i > 0
>>       i = i - 1
>>       yield
>>     end
>>   end
>> end
=> nil
>> 3.my_times {puts 'mangy moose'}
mangy moose
mangy moose
mangy moose

This code opens up an existing class and adds a method. In this case,
the method called my_times loops a set number of times, invoking the
code block with yield . Blocks can also be first-class parameters.

>> def call_block(&block)
>> block.call
>> end
=> nil
>> def pass_block(&block)
>> call_block(&block)
>> end
=> nil
>> pass_block {puts 'Hello, block'}
Hello, block



Class

irb(main):001:0> 4.class
=> Fixnum
irb(main):002:0> 4.class.superclass
=> Integer
irb(main):003:0> 4.class.superclass.class
=> Class
irb(main):004:0> 4.class.superclass.superclass
=> Numeric
irb(main):005:0> 4.class.superclass.superclass.superclass
=> Object
irb(main):006:0> 4.class.superclass.superclass.superclass.superclass
=> BasicObject
irb(main):007:0> 4.class.superclass.superclass.superclass.superclass.superclass
=> nil

Classes start with capital letters and typically use CamelCase to denote capitalization. You must prepend instance variables (one value per object) with @ and class variables (one value per class) with @@ . Instance variables
and method names begin with lowercase letters in the underscore_style . Constants are in ALL_CAPS .

class Tree
  attr_accessor :children, :node_name
  
  def initialize(name, children=[])
    @children = children
    @node_name = name
  end
  
  def visit_all(&block)
    visit &block
    children.each {|c| c.visit_all &block}
  end
  
  def visit(&block)
    block.call self
  end
end

ruby_tree = Tree.new( "Ruby", 
  [Tree.new("Reia"), 
   Tree.new("MacRuby")] )

puts "Visiting a node"
ruby_tree.visit {|node| puts node.node_name}
puts

puts "visiting entire tree"
ruby_tree.visit_all {|node| puts node.node_name}

The attr keyword defines an instance variable. Several versions exist. The most common are attr (defining an instance variable and a method of the same name to access it) and attr_accessor , defining an instance variable, an accessor, and a setter.


Module

A module is a collection of functions and constants. When you include a module as part of a class, those behaviors and constants become part of the class.

module ToFile
  def filename
    "object_#{self.object_id}.txt"
  end
  def to_f
    File.open(filename, 'w') {|f| f.write(to_s)}
  end
end

class Person
  include ToFile
  attr_accessor :name
  
  def initialize(name)
    @name = name
  end
  def to_s
    name
  end
end

Person.new('matz').to_f

What’s interesting here is that
to_s is used in the module but implemented in the class! The class
has not even been defined yet. The module interacts with the including
class at an intimate level. The module will often depend on several class
methods. With Java, this contract is explicit: the class will implement
a formal interface. With Ruby, this contract is implicit, through duck
typing.


The details of Person are not at all interesting, and that’s the point. The
Person includes the module, and we’re done. The ability to write to a file
has nothing to do with whether a class is actually a Person . We add the
capability to add the contents to a file by mixing in the capability. We
can add new mixins and subclasses to Person , and each subclass will
have the capabilities of all the mixins without having to know about
the mixin’s implementation. When all is said and done, you can use a
simplified single inheritance to define the essence of a class and then
attach additional capabilities with modules. This style of programming,
introduced in Flavors and used in many languages from Smalltalk to
Python, is called a mixin. The vehicle that carries the mixin is not always
called a module, but the premise is clear. Single inheritance plus mixins
allow for a nice packaging of behavior.


Enumerable

A couple of the most critical mixins in Ruby are the enumerable and
comparable mixins. A class wanting to be enumerable must implement
each , and a class wanting to be comparable must implement <=> . Called
the spaceship operator, <=> is a simple comparison that returns -1 if
b is greater, 1 if a is greater, and 0 otherwise. In exchange for imple-
menting these methods, enumerable and comparable provide many con-
venience methods for collections.

>> 'begin' <=> 'end'
=> -1
>> 'same' <=> 'same'
=> 0
>> a = [5, 3, 4, 1]
=> [5, 3, 4, 1]
>> a.sort
=> [1, 3, 4, 5]
>> a.any? {|i| i > 6}
=> false
>> a.any? {|i| i > 4}
=> true
>> a.all? {|i| i > 4}
=> false
>> a.all? {|i| i > 0}
=> true
>> a.collect {|i| i
*
2}
=> [10, 6, 8, 2]
>> a.select {|i| i % 2 == 0 } # even
=> [4]
>> a.select {|i| i % 2 == 1 } # odd
=> [5, 3, 1]
>> a.max
=> 5
>> a.member?(2)
=> false

set-based operations: collect, map, inject

>> a
=> [5, 3, 4, 1]
>> a.inject(0) {|sum, i| sum + i}
=> 13
>> a.inject {|sum, i| sum + i}
=> 13
>> a.inject {|product, i| product
*
i}
=> 60

Metaprogramming


Metaprogramming means writing programs that write programs. The ActiveRecord framework that’s the centerpiece of Rails uses metaprogramming to implement a friendly language for building classes that link to database tables.

class Department < ActiveRecord::Base
	has_many :employees
	has_one :manager
end

With the kind of freedom that lets you redefine any class or object at any time, you can build some amazingly readable code.

class NilClass
	def blank?
		true
	end
end

class String
	def blank?
		self.size == 0
	end
end

[ "" , "person" , nil].each do |element|
	puts element unless element.blank?
end

another example:

class Numeric
	def inches
		self
	end

	def feet
		self * 12.inches
	end

	def yards
		self * 3.feet
	end

	def miles
		self * 5280.feet
	end

	def back
		self * -1
	end

	def forward
		self
	end
end

puts 10.miles.back
puts 2.feet.forward

Ruby calls a special debugging method each time a method is missing in order to print some diagnostic information. This behavior makes the language easier to debug. But sometimes, you can take advantage of this language feature to build some unexpectedly rich behavior. All you need to do is override method_missing.

class Roman
	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.X
puts Roman.XC
puts Roman.XII
puts Roman.X

Weakness:
Concurrency and OOP
Object-oriented programming has a critical limitation. The whole prem-
ise of the model depends on wrapping behavior around state, and usu-
ally the state can be changed. This programming strategy leads to
serious problems with concurrency. At best, significant resource con-
tentions are built into the language. At worst, object-oriented systems
are next to impossible to debug and cannot be reliably tested for con-
current environments.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值