热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

ruby丰富多彩的高级功能

这一章节我用了两天,把手头的项目也给放了。终于在今天看完了,但仅限于看完,中间还有很多东东还得需要时间去消化。总结下这一章节中看过的内容,大致的包括:模块的定义与使用、其他文件类的导入、自定

这一章节我用了两天,把手头的项目也给放了。终于在今天看完了,但仅限于看完,中间还有很多东东还得需要时间去消化。总结下这一章节中看过的内容,大致的包括:模块的定义与使用、其他文件类的导入、自定义异常的处理、Proc对象的使用、正则表达式、时间类的使用、线程的定义与使用多线程、垃圾回收机制的介绍。看起来的内容还挺多的,而且都比较重要,所以也不枉用了两天的时间来看,庆幸的是这节的内容之前也有所接触,所以看起来也不会那么的费劲,能够快速的理解

首先一个模块是以前没有见过的,模块使用module来定义,而且不能实例化,如果要用里面定义的实例方法,可以把模块放到一个类里,实例化这个类,然后再使用模块里面的实例方法,如下

module MyModule
  def showModule
    puts "这是定义在module里的实例方法"  
  end
  
  def self.showInfo
    puts "这是定义在module的类方法"
  end
end
#模块不能实例化,所以下面的是不存在的
#mm = MyModule.new
MyModule.showInfo
这是定义在module的类方法
 
 

把模块放到类里面,称之为混含操作(感觉像混蛋操作),这样就要以使用模块里的实例方法了

puts "将模块放入类中,这样就可以调用模块中的实例方法了,称之为混含操作"
class ModuleInclude
  include MyModule
end
mi = ModuleInclude.new
mi.showModule
将模块放入类中,这样就可以调用模块中的实例方法了,称之为混含操作
这是定义在module里的实例方法
模块的另一个比较实用的方法就是实现了相当于命名空间的作用,也就是模块与模块之前是隔开的,不会相互影响

puts "module实现命名空间,同样的方法不会报错"
module FirstModule
  class FirstClass
    def show
      puts "FirstModule里FirstClass的show方法"
    end
  end
end

module SecondModule
  class SecondClass
    def show
      puts "SecondModule里SecondClass的show方法"
    end
  end
end
showMethod = FirstModule::FirstClass.new
showMethod.show
secOndMethod= SecondModule::SecondClass.new
secondMethod.show
module实现命名空间,同样的方法不会报错
FirstModule里FirstClass的show方法
SecondModule里SecondClass的show方法
 可以看到,在模块里有相同的sho方法,但不会报错,就算是相同的类名也不会报错,这个方法虽然在类里也可以实现,但模块应该对系统的开支更小 
 

在一个文件执行时,ruby也提供了类似于前置通知和后置通知的功能,使用BEGIN,END/at_exit。这些比较好理解,只是需要注意的是END的执行顺序要和代码的顺序相反的

puts "程序的执行顺序"
BEGIN{
  puts "程序初始化一"
}
BEGIN{
  puts "程序初始化二"
}
def info
  puts "程序运行,复制文件..."
end
info
at_exit{
  puts "program exit one"
}
at_exit{
  puts "program exit two"
}
END{
  puts "程序正在退出一"
}
END{
  puts "程序正在退出二"
}
程序初始化一
程序初始化二
...
程序正在退出二
程序正在退出一
program exit two
program exit one
可以看到,END和at_exit执行的顺序和代码顺序完全相反,而且不管BEGIN/END出现在文件的什么位置,都会是同样的效果

在实际的开发中,肯定不可能把所有的代码写在一个文件内,这时加载其他文件内的内容就显得非常重要了,加载其他文件有两种方式load/require,load可以加载多次,需要指定文件后缀,但require只能加载一次,否则会返回false,不需要指定文件后缀

puts "使用load加载文件资源"
load "demo/moduleY.rb"
require "#{File.dirname(__FILE__)}/demo/modulea"
mod
加载的moduleY.rb中

module ModuleName
  def show
    puts "a show method in ModuleName"
  end
  
  def self.say
    puts "a static say method in ModuleName"
  end
end
ModuleName.say
加载的modulea.rb中

def mod
  puts "hello, my inviewer!"
end
结果:
使用load加载文件资源
a static say method in ModuleName
hello, my inviewer!
如果只是引入一个module可以用include和extend。这就是上面讲的混含操作,include是指引用地址,extend是引入内容

ruby有一个我暂时认为比较无聊的功能-命名别名

puts "为方法定义别名"
def name
  puts "这是之前的name方法"
end
alias new_name name
def name
  puts "这是重定义name之后"
end
name 
new_name
$old = 3
alias $new $old
puts "命名别名,$old重定义之前",$old,$new
$old = 1
puts "命名别名,$old重定义之后",$old, $new
为方法定义别名
这是重定义name之后
这是之前的name方法
命名别名,$old重定义之前
3
3
命名别名,$old重定义之后
1
1
从上面可以看出来,当定义是不是全局变量的时候相当于把原来的内容放到new_name里面,我能想到的不就是用变量保存内容么?要搞个另外的功能吗?而且全局变量的时候别名和原名只能同时改变,真搞不懂这个的作用是什么

如果需要知道当前的这个变量是什么类型的可以使用defined?来判断,又一个体现动态言语的方法undef出现了,这个可以取消已定义的方法,如果再有调用的就会报错,这让我想到了可以利用这个方法来做黑客的入侵功能,嘿嘿~

class Replay
  def play
    puts "开始播放"
    puts defined? "真的开始了吗 ?"
  end
end
rp = Replay.new
puts defined? rp.play
rp.play
#释放定义后将会出错
#undef play
#puts defined?rp.play
method
开始播放
expression
puts defined?rp.play由于这个play是方法,所以输出的是method。后面的“真的开始了吗?”是一个表达式,所以输出了expression

和代码块差不多,让我感觉比较难的Proc对象,用了我大部分的时间,一看到这个我就不想再继续看下去,但没办法,只能逼着看完

Proc对象一个主要的功能就是把一些重复的东东放在一个块在,又没有作用域的限制,在其他地方都可以调用,估计在以后的开发过程中会大量使用

当一个Proc被new出来的时候,并不会马上执行,只有使用了call方法才会被执行,

puts "Proc对象的使用,使其局部变量对象化的过程"
pr = Proc.new{puts "初始化一个Proc对象"}
pr.call
def call_proc(pr)
  a = "我是在proc对象里的变量"
  puts a
  #调用Proc对象
  pr.call
end
a = "我是在外部声明的变量"
pr = Proc.new{puts a}
pr.call
call_proc(pr)
Proc对象的使用,使其局部变量对象化的过程
初始化一个Proc对象
我是在外部声明的变量
我是在proc对象里的变量
我是在外部声明的变量

调用看起来还是比较简单,而且很好理解,当在一个方法参数里添加&,程序则会把这个形参作为Proc对象来处理

def grab_blc(&blc)
  blc.call
end
grab_blc{puts "这是一个形参的proc对象"}
这是一个形参的proc对象
当需要一个Proc对象时,除了用new来初始化,还能用proc方法来生成

alue = proc do |value|
  puts "使用proc方法创建对象,传递的值是:#{value}"
end
Value.call("使用call传值")
def info
  yield "使用yield代码块传值"
end
info &Value
使用proc方法创建对象,传递的值是:使用call传值
使用proc方法创建对象,传递的值是:使用yield代码块传值
在实际使用中,比如一个实体类会被经常使用到,所以把这个实体类做为一个Proc对象

puts "使用proc来输入实体的属性"
def info(pr)
  pr.call
end
entity = Proc.new{
  @name = "wicky"
  @age = 23
  @gender = "male"
  @hobby = "programming"
  
  puts "name:#{@name}"
  puts "age:#{@age}"
  puts "gender:#{@gender}"
  puts "hobby:#{@hobby}"
}
info (entity)
使用proc来输入实体的属性
name:wicky
age:23
gender:male
hobby:programming
任何一门言语,异常处理机制是不可或缺的,因为程序写的不可能完美,ruby提供的异常处理和其面向对象语言差不多,而且定义的异常也简单的多,包括:RuntimeError/IOError/Errorno::error/NameError/NoMethodError/TypeError/ArgumengError,总共才7个,所以运用起来也方便很多,捕获的方法也很简单,相当于try(){}catcher的,begin/end块来包含可能出现异常的代码,使用rescue来声明捕获的异常,还提供了手动抛出异常的raise和重新运行异常代码块的retry,虽然ruby本身的异常处理机制已经很简单明了,但还是人性化的提供了自定义异常,和自定义异常提示方法。下面是一个异常的模拟

isError = true
begin
  puts "正在请求服务器,请稍后..."
  if isError
    raise "连接超时"
  end
  puts "请求成功"
  
  rescue
    isError = false
    puts "#{$!.to_s},重新请求"
    retry
end
正在请求服务器,请稍后...
连接超时,重新请求
正在请求服务器,请稍后...
请求成功
模拟的是连接服务器的过程,当第一次运行时,isError=true,会进入raise手动抛出异常,并且带有自定义的异常信息,这时程序跳到rescue处理异常,把isError=false模拟连接成功,并且打印出异常信息($!是全局保存异常信息的变量,$@保存全局异常信息的位置等),这时调用了retry重新连接服务器。关于自定义的处理也比较简单,无非就是自己定义一个类,然后继承下Exception就完事了,后面了处理只要把捕获到的异常raise成自己的异常类就行了

编程另外一个非常重要的知识点线程,这是任何程序猿都必须知道的事,ruby的线程操作非常简单,大大简化了线程的操作过程,而且提供了很多有用的库供使用

i = 1
thread = Thread.new do
  10.times { |count|
    puts "当前线程#{count}"
  }
end
thread2 = Thread.start do
  puts "这是线程2"
end
#不加入线程则,无法打印出信息
thread.join
thread2.join
当前线程0
当前线程1
当前线程2
当前线程3
当前线程4
当前线程5
当前线程6
当前线程7
当前线程8
当前线程9
这是线程2
不知道是不是版本的问题,我用的是1.9.3必须得把这个线程join到当前运行的线程中才可以打印信息,但有些版本好像可以不用join,真搞不懂
ruby提供了一个非常好的类来处理著名的售票问题

@num = 200
@mutex = Mutex.new
def ticketSeller(tickets)
  @mutex.lock
  Thread.pass
  if (@num >= tickets)
    @num -= tickets
    puts "您购买#{tickets}已成功"
  else
    puts "出票失败,没足够的票,当前余票#{@num}" 
  end
  @mutex.unlock
end
buyer1 = Thread.new(199){|num| ticketSeller(num)}
buyer2 = Thread.new(2){|num| ticketSeller(num)}
buyer1.join
buyer2.join
您购买199已成功
出票失败,没足够的票,当前余票1
Mutex提供了一个线程锁,能够把当前的这个块锁定,不让其他线程访问,这样就可以保证数据的正确性,直到调用unlock方法,另外线程还提供了pass/sleep/join/wakeup/current等显示/休眠/挂起/唤醒/当前线程方法,如果需要查询当前的线程状态可以用到status。拥有的值有:sleep/run/aborting/false/nil表示休眠、运行、被取消、正常终止、被非正常终止等。另一个比较常用的库SizedQueue也非常有用,能够让线程按顺序执行,而不用担心同步问题

在这节的学习中,是学习ruby为止比较重要而且比较难的部分,写的代码也越来越多,越来越复杂,一些常见的基础知识也基本用到了,接下来就得了解ruby的IO操作为对数据库的操作了,看完这两个比较重要的内容准备着手ROR,毕竟不断的挑战难点才能回首现在,让现在难以理解或使用的知识自动变的简单

KEEPPING UP



推荐阅读
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 深入解析Linux下的I/O多路转接epoll技术
    本文深入解析了Linux下的I/O多路转接epoll技术,介绍了select和poll函数的问题,以及epoll函数的设计和优点。同时讲解了epoll函数的使用方法,包括epoll_create和epoll_ctl两个系统调用。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • 禁止程序接收鼠标事件的工具_VNC Viewer for Mac(远程桌面工具)免费版
    VNCViewerforMac是一款运行在Mac平台上的远程桌面工具,vncviewermac版可以帮助您使用Mac的键盘和鼠标来控制远程计算机,操作简 ... [详细]
  • web.py开发web 第八章 Formalchemy 服务端验证方法
    本文介绍了在web.py开发中使用Formalchemy进行服务端表单数据验证的方法。以User表单为例,详细说明了对各字段的验证要求,包括必填、长度限制、唯一性等。同时介绍了如何自定义验证方法来实现验证唯一性和两个密码是否相等的功能。该文提供了相关代码示例。 ... [详细]
  • VueCLI多页分目录打包的步骤记录
    本文介绍了使用VueCLI进行多页分目录打包的步骤,包括页面目录结构、安装依赖、获取Vue CLI需要的多页对象等内容。同时还提供了自定义不同模块页面标题的方法。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 本文介绍了Sencha Touch的学习使用心得,主要包括搭建项目框架的过程。作者强调了使用MVC模式的重要性,并提供了一个干净的引用示例。文章还介绍了Index.html页面的作用,以及如何通过链接样式表来改变全局风格。 ... [详细]
  • Node.js学习笔记(一)package.json及cnpm
    本文介绍了Node.js中包的概念,以及如何使用包来统一管理具有相互依赖关系的模块。同时还介绍了NPM(Node Package Manager)的基本介绍和使用方法,以及如何通过NPM下载第三方模块。 ... [详细]
  • 本文介绍了Python函数的定义与调用的方法,以及函数的作用,包括增强代码的可读性和重用性。文章详细解释了函数的定义与调用的语法和规则,以及函数的参数和返回值的用法。同时,还介绍了函数返回值的多种情况和多个值的返回方式。通过学习本文,读者可以更好地理解和使用Python函数,提高代码的可读性和重用性。 ... [详细]
  • Linux下安装免费杀毒软件ClamAV及使用方法
    本文介绍了在Linux系统下安装免费杀毒软件ClamAV的方法,并提供了使用该软件更新病毒库和进行病毒扫描的指令参数。同时还提供了官方安装文档和下载地址。 ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • 第七课主要内容:多进程多线程FIFO,LIFO,优先队列线程局部变量进程与线程的选择线程池异步IO概念及twisted案例股票数据抓取 ... [详细]
author-avatar
啥也不会
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有