最近突然有兴趣翻开了队友从队里順来的一本书Bruce.A.Tate的《七周七语言 理解多种编程范式》,正好实训划水无事可干,打算写一个系列,涉及Ruby Lo Prolog Scala Erlang Clojure Haskell,对他们的特性,编程模型做一个简要的理解。语法一笔带过,日后详细学习的日后再补上
开始
- 环境:Archlinux
- 编辑器:VsCode
- 菜鸟教程Ruby
- Ruby官方中文文档
安装Ruby
安装ruby:sudo pacman -S ruby
安装irb交互shell:sudo pacman -S irb
安装成功后,查看版本号,执行第一个hello world程序:$ruby -v
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]
$irb
irb(main):002:0> puts 'hello world'
hello world
=> nil
和python类似,ruby有解释模式和交互模式,ruby脚本常以*.rb
命名,和python一样需要指定编码,否则中文输入会出现问题:
# -*- coding: UTF-8 -*-
puts "你好,世界!";
存为hello.rb
可以用ruby hello.rb
解释执行
基本语法
BEGIN END代码块
|
BEGIN,END会分别在主程序的执行前/后执行,输入结果如下:初始化 Ruby 程序
这是主 Ruby 程序
停止 Ruby 程序
Here Document
详细见:菜鸟教程
注释
类似python等脚本语言,ruby的行注释也是#
# i am comment
但ruby支持块注释=begin
i am comment
=end
容器
数组
ary = [ "fred", 10, 3.14, "This is a string", "last element", ] |
ruby的数组是广义表,类似js,创建非常方便,且支持pop
,push
ruby的数组访问和python,R一样灵活,支持副负数下标逆序访问,也支持区间访问。
Map
"me"=>"XUranus","renying"=>"thankod"} dic = { |
类似py的字典,语法糖更好看
判断
'awesome man' unless 1==2 puts |
ruby的条件判断语法糖极其丰富,接近自然语言甚至支持倒装句!但是要注意if后面的判断除了nil
和false
全是true
,即使是0也是true
!!!
类 方法
一个Tree容器class Tree #类名首字母大写
attr_accessor :children, :node_name #定义实例成员变量
def initialize(name,children=[]) #类似py的optional param 和py的区别在于没有:
@children=children
@node_name=name #成员变量赋值用@代替this
end
def visit(&block) #&不是表示去引用,而是表示这个是一个代码快
block.call self
end
def visit_all(&block)
visit(&block)
children.each {|x|x.visit_all &block}
end
end
ruby_tree = Tree.new("Ruby",[Tree.new("scala"),Tree.new("java")])
ruby_tree.visit_all {|node|puts node.node_name}
打印出Ruby
scala
java
特性
Ruby 可以用来编写通用网关接口(CGI)脚本,可以被嵌入到超文本标记语言(HTML),可以轻易链接数据库,有丰富的内置函数和GUI工具。
真正面向对象
Ruby是一门真正面向对象的语言,java在基本类型(double,float…)还是用了过程语言的写法,有时需要Double来解包。而ruby即使是基本类型也是对象:4.class
=> Integer
2==3).class (
=> FalseClass
4.class.superclass
=> Numeric
在全局环境中,默认当前是main函数 self
=> main
self.class
=> Object
self.class.superclass
=> BasicObject
self.class.superclass.superclass
=> nil
强类型
尽管ruby是一门脚本语言,但ruby是强类型的!这一点和js的不一样,相比用不报错的js这一点的确很好。脚本语言需要在运行时候做类型检查,ruby充分的平衡了运行时类型检查和语言灵活性。'hello ' a =
=> "hello "
1 b =
=> 1
a+b
Traceback (most recent call last):
5: from /usr/bin/irb:23:in `<main>'
4: from /usr/bin/irb:23:in `load'
3: from /usr/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
2: from (irb):19
1: from (irb):19:in `+'
TypeError (no implicit conversion of Integer into String)
可见不同的类型是不能操作的
代码块与函数
Ruby中函数也是以及公民,可以被当作参数传递,写个遍历容器的例子:"fred", 10, 3.14, "This is a string", "last element", ] ary = [
=> ["fred", 10, 3.14, "This is a string", "last element"]
|x|puts x} ary.each{
fred
10
3.14
This is a string
last element
这种类似js的arr.foreach(x=>console.log(x))
,然而我觉得这种=>
要好看的多,|x|
的写法很丑,但是Rust好像也用了这种语法糖!
下面这种写法也等价,(看似这种以do end封装的代码快类似一种匿名函数?且享有执行区的作用域?)|i| ary.each do
irb(main):010:1* puts i
end
fred
10
3.14
This is a string
last element
=> ["fred", 10, 3.14, "This is a string", "last element"]
鸭子类型
上个例子中["fred", 10, 3.14, "This is a string", "last element"]
,我们可以发现这个容器是一个广义表序列,ruby不需要容器具有相同的类型,实际上此处并不是执行了动态类型推断,而是得易于ruby的面向对象机制,实际上此处的对象都有着共同的基类Object。
当我们执行力puts
输出后,其实是执行了他们的共有的to_s()
方法提取了字符串化的输出信息,“只要他走路像鸭子,他能嘎嘎叫,他就是鸭子”,这就是ruby的鸭子类型。ruby的数组支持pop
,push
,所以就可以当作栈来用。
ruby是在运行时进行动态类型推导的,但是这也带来了问题def hello
'hello'+42
end
=> :hello
hello
Traceback (most recent call last):
6: from /usr/bin/irb:23:in `<main>'
5: from /usr/bin/irb:23:in `load'
4: from /usr/lib/ruby/gems/2.6.0/gems/irb-1.0.0/exe/irb:11:in `<top (required)>'
3: from (irb):15
2: from (irb):13:in `hello'
1: from (irb):13:in `+'
TypeError (no implicit conversion of Integer into String)
这个例子中,方法定义时没有做类型检查,只有运行时才发现错误,导致了ruby不能有效的抛出错误。
运算符
执行基本的加法运算4+3
,因为ruby是面向对象语言,所以这里+
是一种add方法,他的名字叫+
,实际上调用是调用的4.+(3)
4.+(3)
=> 7
可以用.methods
查看一个类型的方法4.methods
=> [:-@, :**, :<=>, :upto, :<<, :<=, :>=, :==, :chr, :===, :>>, :[], :%, :&, :inspect, :*, :+, :ord, :-, :/, :size, :succ, :<, :>, :to_int, :coerce, :to_s, :to_i, :to_f, ....]
开放类
class NilClass #NillClass和String都是ruby的内建基类 |
irb(main):025:0> nil.blank? |
ruby不像java,需要继承一个类,并重写某个方法来改变类的特性。ruby支持开放类,class XXX
申明某个类,如果没有被定义则定义该类,如果定义了就增加/重写这个类的某个方法。
Mixin
面向对象复用代码的一个典型方法就是继承,但是要同时继承多个类的行为要用到多继承,事实证明这会带来很多问题,所以java采用了单继承+接口的策略,而ruby则是采用模块。模块是函数和常量的集合,这点很像scala的traitmodule ToFile
def file
"object_#{self.object_id}.txt"
end
def to_f
File.open(filename,'w') {|f|f.write(to_s)} #注意这里的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
这里我们发现模块ToFile的to_f中调用了to_s,但是to_s是在调用了他的类里之后定义的,然而这里并不会报错,这说明了模块中可以调用可能会使用他的类的未实现的方法!!!然而这里定义模块的时候ruby显然是没有进行静态检查的,体现了ruby的鸭子类型,运行时才会进行检查。
总结
Ruby的优点在与灵活性,提升了程序员的编程体验。和大多脚本语言一样,Ruby适用与做胶水语言,适合做爬虫。Ruby的web框架Rail也取得了巨大的成功。但Ruby的不足主要表现在:
- 性能:ruby最大的弱点就是性能,但Robinius虚拟机已经用上了JIT,ruby1.9已经比上个版本快了十倍多。
- 并发:面向对象的通病,面向对象的有状态性导致在并发条件下有很大的问题。
- 类型安全:ruby是鸭子类型的支持者,无法在运行前做类型检查。例如mixin可以在对象没有创建时就调用其方法,这导致了一个问题就是ruby的IDE很难编写,市面上少有人开发ruby的IDE。