Try Ruby

编程/技术 更新时间: 2019-06-16 @ 08:46:04 创建时间: 2019-06-16 @ 08:46:04 浏览数: 265 净访问: 213 By: skyrover

本博客采用创作共用版权协议, 要求署名、非商业用途和保持一致. 转载本博客文章必须也遵循署名-非商业用途-保持一致的创作共用协议


对程序员友好的语言难道不值得一学吗?程序员最好的朋友……

ruby安装

  • rbenv
sudo apt-get install -y libssl-dev libreadline-dev zlib1g-dev
git clone git://github.com/sstephenson/rbenv.git ~/.rbenv
git clone git://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
git clone https://github.com/andorchen/rbenv-taobao-mirror.git ~/.rbenv/plugins/rbenv-taobao-mirror

下载完之后需要将~/.rbenv/bin加到系统PATH

# 查看所有的可以安装的版本
rbenv install --list
# 安装当前最新版(2019.4.14)
rbenv install 2.6.2

安装完成后将eval "$(rbenv init -)"加入zshrc或者其他文件中,然后输入下面命令rbenv global 2.6.2,再键入命令:ruby -v,就可以看到:

ruby 2.6.2p47 (2019-03-13 revision 67232) [x86_64-linux]

这时候ruby就安装成功了

  • irb(终端形式)
  • gem
  • bundler

相关网站

读的书是向Ruby之父学程序设计,Ruby之父是个日本人:松本行弘。家里有一本松本行弘的程序世界准备在看完Ruby之后拜读一下~

看完本书第一章,其实感觉和Python很像,都是动态语言,语法简洁,读起来比较自然。

第一章 与Ruby的第一次接触

字符串:使用单引号不会解译\n之类的特殊字符,和shell中的一样

ruby中方法允许省略(),所以有print "hello ruby.\n",并且print后面不会自动换行,而puts方法会自动换行

ruby中p方法会将要打印的对象完整的显示出来,比如字符串会加上双引号之类的,对应python中的reprstr,另外还有个pp方法,即pretty print

导入一个包使用include,比如include Math,也可以不导入,直接使用Math.sin()

注释方面都是用#,但是ruby可以定义长篇注释:

=begin
第一章 与Ruby的第一次接触
=end
x = 10

条件判断,感觉是和shell的条件判断比较像,但是跟简单点:

if a >= 10 then
  print("bigger\n")
else
  print("smaller\n")
end
# then可以省略
if a >= 10
  print("bigger\n")
end

循环有while语句和times方法

i = 1
while i <= 10
  print(i, "\n")
  i = i + 1
end

# 反复次数+反复动作
100.times{
    print("loop")
}

定义函数

def hello
  print("hello, ruby")
end

读入其他文件

require "hello"

第2章 方便的对象

数组

names = ["小林", "林"]
names[0]
names.size # 数组大小
names.each{|n|
  print(n, "\n")
}

hash

类似python中的map

font_table = {"normal" => "+0", "small" => "-1", "big" => "+1"}
# 和python一样,没有顺序
font_table.each{|key, value|
  print("each ", key, " ", value)
}

正则表达式

样式:/ruby/,运算符:=~

# 成功返回匹配到的位置,失败返回nil
p /Ruby/ =~ "Ruby"
p /Ruby/i =~ "ruby" # 不区分大小写,而且注意到//之间的字符串是没有双引号的

第3章 指令设计

ARGV[0] 表示第一个参数,传递进来的都是字符串,转换为数值:ARGV[0].to_i

读取文件

filename = ARGV[0]
file = open(filename)
# gets方法一次读一行字符串,读到结尾返回nil
while text = file.gets do
  print text
end
file.close

正则表达式的pattern需要提前定义好,或者中途需要创建一个新的对象,而不能是//之间直接加上字符串

pattern = Regexp.new(ARGV[0])
filename = ARGV[1]

file = open(filename)
while text = file.gets do
  if pattern =~ text
    print text
  end
end
file.close

第4章 对象与变量、常数

类表示对象的类型,类是对象的雏形。Numeric, String, Array, Hash, Regexp, File

局部变量,全局变量($开始),实例变量(@开始),类变量(@@开始),虚拟变量(true, false, self特定名称变量)

对象标识可以通过object_id方式获得,通过equal?方法判定是否同一对象,判断对象值是否相等,使用==,还有一个eql?进行判断,类似JS中的三等和双等,eql?判断同时也会比较类型,而==只要值相等就会判断相等

str1 = "foo"
str2 = str1
str1.equal?(str2) # true
str = "f" + "o" + "o"
str1 == str # true
1.0 == 1 # true
1.0.eql?1 # false

第5章 条件判断

条件判断有三种:if语句,unless语句,case语句

字符串empty?方法可以判断字符串长度是否为0:"".empty?为true

一般在定义返回真假值的方法时候,后面会加上?字符

&&||将多个条件连接在一起,另外and,or,not也可以使用,但是优先级比较低

if x >= 1 && x <= 10
...
end

if语句 then可以省略 注意elsif 而不是elif

if cond1 then
  ...
elsif cond2 then
  ...
else
  ...
end

unless语句

unless是和if相反的语句 then可以省略

unless a > b then
  print "a小于b"
else
  print "a大于等于b"
end

case语句 then可以省略

case 想要比较的对象
when 1 then
  语句1
when 2 then
  语句2
else
  语句4
end

一个处理电子邮件头部信息的例子

while line=gets
  case line
  when /^From:/i
    print "寄件人"
  when /^To:/i
    print "收件人"
  when /^Subject:/i
    print "主题"
  when /^$/i
    print "头部结束"
    exit
  else
    # 跳过
  end
end

array = ["aa", 1, nil]
item = array[0]
case item
when String
  print "item is a string"
when Numeric
  print "item is a numeric"
else
  print "item is a something"
end

tags = ["A", "IMG", "PRE"]
tags.each{|tagname|
  case tagname
  when "P", "A", "I", "B", "BLOCKQUOTE"
    print tagname, " has child.\n"
  when "IMG", "BR"
    print tagname, " has no child\n"
  else
    print tagname, " cannot be used\n"
  end
}

类似Python中的,if也可以写在语句后面,不过不用写else: print "a > b" if a > b

when后面的条件其实相当于===,用来判定是否等同,when后面是数值或者字符串,即相当于==,判断相等,正则表达式则相当于匹配判定,类的话,判断是否是类的实例。而且都会在符号左边,要比较的在右边

第6章 循环

循环专用的语句和方法:times, each, loop, for, while, until

4.times{
  print "4"
}

# 也可以这样写
4.times do
  print "4"
end

# 使用|i|将循环次数记到变量里
4.times{ |i|
  print "4", i, "\n"
}

for 语句

sum = 0
for i in 1..5
  sum = sum + i
end

# do可以省略 包含结束数值
# 如果是三个点 1...5 则不包含结束值
for i in 1..5 do
  sum = sum + i
  sum += i
end
print sum, "\n"

一般的for语句

names = ['a']
for name in names
  print name
end

while语句

i = 1
while i < 3
  print i, "\n"
  i += 1
end

until语句,和while语句同样条件会是相反的,只有在条件不成立的时候反复执行,所以可以while !(sum>=50)

sum = 0
i = 1
# do可以省略
until sum >= 50 do
  sum += i
  i += 1
end
print sum, "\n"

each方法

names = ["awk"]
names.each{|name|
  print name, "\n"
}

names.each do |name|
  print name, "\n"
end

ruby语言each为基本的循环方式,for则是使用each实现的语法

sum = 0
(1..5).each{|i|
  sum = sum + i
}
print sum, "\n"

loop方法,不断进行循环处理

loop {
  print "Ruby"
}

循环控制:break, next(类似continue), redo(以相同条件重新执行该循环)

# redo
["perl", "python", "ruby", "scheme"].each{|lang|
  i += 1
  if i == 3
    redo
  end
  p [i, lang]
}
# 会输出1,2,4,5

# next
file = open(ARGV[0])
while text = file.gets
  next if /^\s*$/ =~ text
  next if /^#/ = ~ text
  print text
end

第7章 方法

  • 实例方法
  • 类方法
  • 函数性的方法

没有接收者的方法,称为函数性的方法

定义方法

def hello(name)
  print("Hello, ", name, ".\n")
end

基本上和Python都类似

第8章 类与模块

arr = Array.new
p arr
p arr.class
p arr.instance_of?(Array)

ruby语言中所有类都是Object类的子类,Object是唯一没有父类的类。使用is_a?来检查有继承关系的父类

str = "string"
p str.is_a?(String)
p str.is_a?(Object)

ruby里面内建类的继承关系

自己定义类

class HelloWorld
  def initialize(myname="Ruby")
    @name = myname
  end

  def hello
    print "Hello World. I am ", @name, ".\n"
  end
end

bob = HelloWorld.new("Bob")
ruby = HelloWorld.new

ruby 不允许从对象外部直接读取,写入实例变量,所以要访问对象内部数据,需要定义方法来操作

class HelloWorld
  def initialize(myname="Ruby")
    @name = myname
  end

  def hello
    print "Hello World. I am ", @name, ".\n"
  end

  def name
    return @name
  end

  def name=(value)
    @name = value
  end
end

bob.name = "Robert"
# 实际上进行了name=("Robert")方法调用

定义了attr_readerattr_writer, attr_accessor来快速定义方法,来访问实例变量

class HelloWorld
  attr_accessor :name
end

类方法

class HelloWorld
  def HelloWorld.hello(name)
    print name, " said hello."
  end
end

class HelloWorld
  ...
end

class << HelloWorld
  def hello(name)
    print name, " said Hello"
  end
end

class HelloWorld
  def self.hello(name)
    print name, " said hello"
  end
end

还是Python中的classmethod比较优雅。访问方式:类.方法, 类::方法

常数

class HelloWorld
  Version = "1.0"
end

类变量

@@开始,同样类变量要在外部访问,也必须定义访问方法

class HelloCount
  @@count = 0

  def HelloCount.count
    @@count
  end

  def initialize(myname="Ruby")
    @name = myname
  end

  def hello
    @@count += 1
    print "hello world, i am ", @name, ".\n"
  end
end
# 是否可以通过实例来访问类变量?
bob = HelloCount.new("Bob")
bob.hello
bob.count
p HelloCount.count

扩充类

在已经定义的类中新增方法

class String
  def count_word
    # 和Python一样,使用self来表示对象
    ary = self.split(/\s+/)
    return ary.size
  end
end

继承

class RingArray < Array
  # 居然数组索引a[1]中[]运算符直接就是一个方法,这也太牛逼了吧
  def [](i)
    # array有size属性,所以在类中可以直接调用
    idx = i % size
    super(idx)
  end
end

限制方法的调用

  • public
  • private 不能指定方法接收者,接收者只能是self,且self必须省略!不允许接在接受者后面调用,initialize方法恒为private
  • protected 可以在本类或者子类中访问,可以以实例方法的方式调用
class AccTest
  def pub
    puts "hello"
  end

  public :pub

  def priv
    puts "private"
  end

  private :priv
end

# 对多个方法进行访问限制
class AccTest1
  public

  def pub
    puts "pub"
  end

  private
  def priv
    puts "priv"
  end
end

下面一个类可以访问,但是不能更改

class Point
  attr_accessor :x, :y
  protected :x=, :y=

  def initialize(x=0.0, y=0.0)
    @x = x
    @y = y
  end

  def swap(other)
    # 居然不能像Python一样交换
    xtemp = @x
    ytemp = @y
    @x = other.x
    @y = other.y
    other.x = xtemp
    other.y = ytemp
    # 返回当前对象
    self
  end
end

什么是面向对象

以面向对象的方式写程序,可以更容易的面对数据的变更,要不然总是改处理程序。所以对象除了具有数据之外,还有行为。

使用面向对象封装的数据,暴露的接口不变,后面的处理就可以不变,哪怕基础数据结构有变化,也通过了封装给处理了。

面向的对象的特征

  • 封装
  • 多态,对于不同的消息有自己的处理方法】
  • 继承

鸭子类型

对象的特征是根据对象具有什么行为来决定的,所以叫行为决定类型

# 这个是什么操作。。
def fetch_and_downcase(ary, index)
  if str = ary[index]
    return str.downcase
  end
end

实际上是不同的东西,但是只要能够进行相同操作,就能让处理方式统一化。比较容易让没有继承这类明确关系的对象之间能够统一化其处理方式。

模块:程序部分的集合体

  • 提供命名空间

使用模块名.方法名方式调用,使用include将模块的方法和常数名读入到现在的命名空间里

  • 以Mix-in方式提供功能

类的定义内使用include,可以将模块中定义的方法和常数纳入类定义中

module MyModule
end

class MyClass1
  include MyModule
end

Mix-in有相似功能,但是不想归于某个类。Ruby没有多继承,使用mixin实现多继承。mixin中原有类中的self会是mixin目标类的实例。

自己定义模块

module HelloModule
  Version = "1.0"
  def hello(name)
    print "hello"
  end

  # 以模块函数公开
  module_function :hello
end

在模块函数中引用self,可以获得模块本身,这样就可以使用模块函数改写模块本身状态,一般用于mixin。所以专门为mixin设计的模块就不应该提供模块函数。

第9章 错误处理与例外

其实整体上和Python很是类似

begin
  有可能发生异常的动作
rescue => 存放异常对象的变量
  异常处理动作
end
  • $! 最后发生的异常对象
  • $@ 最后发生异常的位置信息

异常对象的方法:

  • class:异常类别
  • message:异常消息
  • backtrace:异常的位置信息$@=$!.backtrace
ltotal = 0
wtotal = 0
ctotal = 0
ARGV.each{|file|
  begin
    input = open(file)
    l = 0
    w = 0
    c = 0
    while line = input.gets
      l += 1
      c += line.size
      line.sub!(/^\s+/, "")
      arg = line.split(/\s+/).size
      w += ary.size
    end
    input.close
    ltotal += l
    wtotal += w
    ctotal += c
  rescue => ex
    print ex.message, "\n"
  end
}

和Python中finally类似的ensure

def copy(from, to)
  src = open(from)
  begin
    dst = open(to, "w")
    data = src.read
    dst.write
    dst.close
  ensure
    src.close
  end
end

retry重新执行

file = ARGV[0]
begin
  io = open(file)
rescue
  sleep 10
  retry
end
data = io.read
io.close

rescue修饰符 这个很强大

# 前者发生异常,则表达式等于rescue右面的值
n = Integer(val) rescue 0

第10章 数值Numeric类

  • Integer
    • Fixnum(普通整数)
    • Bignum(大整数)
  • Float(浮点小数)

  • 八进制 0123

  • 十进制 123 or 0d123
  • 十六进制 0x123
  • 二进制 0b1111011
  • 字符a的ASCII码 ?a
  • 浮点数指数写法 1.23e4
ans = x.divmod(y)
x == ans[0] * y + ans[1]
p 10.divmod(3.5)  # => [2.0, 3.0]
# 还有两个,都是求余数的
x.modulo(y)  # x % y
x.remainder(y)  # 余数,但是与x同号

Math模块

include Math

数值转换

  • to_i 转为Integer
  • to_f 转为Float
  • round 四舍五入
  • ceil
  • floor

位运算

  • ~ 位反转,非
  • &
  • |
  • ^ 异或
  • <<
  • >

数数

  • 10.times{|i|}
  • 2.upto(10){|i|}
  • 10.downto(2){|i|}
  • 2.step(10, 3){|i|}

数组(Array)类

建立数组

num = [1, 2, 3]
a = Array.new
a = Array.new(3)  # [nil, nil, nil]
a = Array.new(3, 0)  # [0, 0, 0]

# 元素是字符串,而且不包含空白
lang = %w(Ruby Perl Python)  # ["Ruby", "Perl", "Python"]

# to_a方法
color_table = {"black" => "#000000"}
color_table.to_a  # => [["black", "#000000"]]

# split 方法
column = "1 2 3".split()
# ["1", "2", "3"]

索引

  • a[n]
  • a[n..m] 或者 a[n...m] 建立新数组返回
  • a[n, len] 从n处获取len个元素,建立新数组返回

另外一些方法

  • a.at(n)
  • a.slice(n) 等同 a[n]
  • a.slice(n..m) a[n..m]
  • a.slice(n, len) a[n, len]

插入元素

a[2, 0] = ["x"] 在2位置替换0个元素,也就是插入元素

以多个索引创建新数组

a.values_at(1, 2, 5)

Array可以直接做集合运算

也可以直接做列表运算

  • unshift 加入操作 前端
  • shift 取出元素 前端
  • first 读取第一个元素
  • push 相当于 a << item
  • pop
  • last

a.concat(b)等同于a+b,但是+会返回新数组

删除数据

  • a.compact 从a中删除nil元素,会建立新数组
  • a.compact! 直接改写原有数组
  • a.delete(x)
  • a.delete_at(n)
  • a.delete_if{|item|...}
  • a.reject{|item|}
  • a.reject!{|item|}

加上后缀!直接改写接收者对象的破坏性方法

a = [1, 2, 3]
a.collect!{|item| item * 2}
  • a.fill
  • a.flatten
  • a.reverse
  • a.sort
  • a.sort_by
a = Array.new(3) {
  [0, 0, 0]
}

result = []
ary1.zip(ary2, ary3) {|a, b, c|
  result << a + b + c
  i += 1
}

方法真多,得多用才能记得住。。

第12章 字符串(String)类

字符串中被#{}框柱的部分会被当成Ruby语句执行,并且将运算结果插入

moji = "字符串"
str1 = "也是#{moji}"

%Q相当于被双引号括住的字符串,%q相当于被单引号括住的字符串

desc = %Q{Ruby字符串}

嵌入文档,<< 来自Unix

printf和sprintf

获取字符串长度:length和size

length是返回的字节数,ruby中的字符串是字节川,与索引相关联的方法中,是以字节为单位进行字符串处理的。

p '面向对象程序语言'.split(//u).length 返回的是8

分割字符串

有两种方法:

  • 以特定字符分割字符串
str = "ruby:is:a:language"
column = str.split(/:/)
  • 将字符串以特定字数分割

使用unpack,pack用来将数组对象转换成二进制数据,unpack将二进制数据复原成数组

str = "Ruby In A Nutshell Yukihiro Matsumoto 2001USA"
column = str.unpack("a20a20a4a*")
p column

连接字符串

+创建新的字符串,concat或者<<,在现有字符串接上新的字符串

hello = "hello"
world = "world"
hello << world

字符串索引

ruby是以字节串来表示字符串的,所以使用索引获取的对象是一个字码,如果想用字符串表示这个对象,使用chr方法

在最新版本的Ruby中已经不是这样了,直接返回的就是字符串对象

str = "abcdef"
p str[0]  # 97
p str[0].chr  # "a"

如果是中文字符串,那么需要先split,然后再索引

str = "甲乙"
p str.split(//u)[0]

如果一次获取特定长度,那么返回的是字符串对象

str = "甲乙丙"
p str[1, 1]

处理换行字符

chop必须删除一个字符

  • chop:非破坏性,创建新的字符串
  • chop!:破坏性
  • chomp: 只删除换行字符
  • chomp!

字符串查找和取代

  • index
  • rindex
  • include?

换行相关:

LF:line feed编码为0X0a,CR:Carriage Return,编码为0X0d

  • Unix: LF
  • Windows:CR+LF
  • Mac OS: CR

字符串取代:

  • sub
  • gsub 全局取代

  • s.reverse()

  • s.strip

第13章 杂凑(HASH)类

建立hash

h1 = {"a" => "b", "c" => "d"}
p h1["a"]

# 默认初始值为nil
h2 = Hash.new
h3 = Hash.new("")
h3.store("R", "Ruby")
# fetch如果不存在会发生异常
h3.fetch("R")
h3.fetch("R", "(undef)")
# fetch的参数是区块
h3.fetch("N") { String.new }
h = Hash.new{|hash, key|
  hash[key] = key.upcase
}

判断是否存在

  • h.key?(key)
  • h.has_key?(key)
  • h.include?(key)
  • h.member?(key)
  • h.length
  • h.size
  • h.empty?
  • h.delete
  • h.delete_if{|key, value| key == "P"}
  • h.clear

计算单字数

count = Hash.new(0)

while line = gets
  words = line.split
  words.each{|word|
    count[word] == 1
  }
end

count.sort{|a, b|
  a[1] <=> b[1]
}.each{|key, value|
  print "#{key}: #{value}\n"
}

<=>在小于时候返回负值,=返回0,大于返回正值


点赞走一波😏


评论

提交评论