Project1

标题: [长期更新]秀秀之走火入魔的高级脚本教程(至五章二节) [打印本页]

作者: hide秀    时间: 2008-11-1 06:26
标题: [长期更新]秀秀之走火入魔的高级脚本教程(至五章二节)
本帖最后由 忧雪の伤 于 2012-1-2 19:08 编辑

秀秀之走火入魔的高级脚本教程
[LINE]1,#dddddd[/LINE]
  写这个教程的时候自己已经从地狱里走出来了
  为什么这么说 首先可以确定这个教程容易走火入魔 哈哈
  因为有些东西真的是很难 经过长时间的钻研
  
  和不停的测试 终于把思路理清楚了 所以决定写出来共享

  RMXP 出来估计已经有三年多了吧 很多人已经对脚本非常熟悉
  
  并且能开发独立系统 但是并非对脚本(RUBY语言)理解的透彻
  
  有些难以理解的语法知识可能还是盲点 有的时候是不是看别
  
  人的脚本 大致能看懂 就是有一点地方搞不清楚的地方 所以
  
  这个教程就是针对一些难点来讲 并且配合范例 彻底将大家心
  
  头的难点解开
  
  大概这个教程分为11个部分(可能以后还会添加)
   
    I:    变量
   
    II:   一切皆为对象
   
    III:  类,超类,对象,模块
   
    IV:   字符串,符号,范围
   
    V:    方法 , proc
   
    VI:   内部类(模块)扩展   
   
    VII:  试着重写内部方法

    VIII: 数据结构,存储
   
    IX:   难以理解的内部方法分析
   
    X:    线程

    XI:   正则表达式范例式分析
   
  因为是将难点 所以一些基础知识就不讲了 如类怎么定义之类的
  这些可以参考 F1
  在讲之前还请各位做好思想准备 如基础不扎实 很有可能走入误区
  或者看到崩溃也无法理解? 正常 XD.... 因为是很难的东西嘛
  我在此之前也是看过很多夏娜的脚本 从中悟出些东西
  当你如能顺利的看懂 你的思路应该相当清晰了
  可以写出一些出乎意料的脚本(当然教程中 我会写点临时的脚本)
  如有点其他语言的基础 可以尝试开发些ruby的引擎 (如夏娜的RGE...= =)
  哈哈 好了 那我们正式开始第一章吧
  
  =====================================
  I 变量
  =====================================
  
  变量似乎没什么好说的 局部变量 全局变量 可能大家非常清楚
  我就说类变量(@@开头)和实例变量吧(@开头)
  两者区别(一定要记住):
  实例变量是对象的独享的变量 当生成了一个类的对象 这个类中的实例变量
  则为这个对象独有 RM默认的类 比如 Game_Battler 类中有很多实例变量
  @hp,@sp之类的 每个 Game_Battler 对象都会拥有不同的 @hp,@sp值
  实例变量相信大多数人都很清楚
  类变量是类中的共享的变量 这个类的所有的对象都共享这个类中的类变量
  所以类变量的值是唯一的(只有一个内存地址)
  区别很明显可以看出来吧
  实例变量如果定义类中 那只有类才能访问 方法是无法访问的
  
  [范例1.1]
  
  class Abc
    @a = 0
    p a
  end  
  
  显示结果 1
  
  class Abc
    @a = 0
    def view_var
      return @a
    end  
  end
  
  a = Abc.new
  p a.view_var
  
  显示结果 nil
  由于view_var返回的是在类中定义的实例变量@a,所以方法中返回为nil
  当某个对象更改了当前类的类变量的值 那其他对象的访问类变量的值也改变了
  
  具体看个范例
  
  [范例1.2]
  
  class Abc
    @@class_var = 0
    def change_class_var(value)
      @@class_var = value
    end
    def view_class_var
      return @@class_var
    end  
  end  
   
  a = Abc.new
  p a.view_class_var
  b = Abc.new
  b.change_class_var(1)
  p a.view_class_var
  
  结果:
  0
  1
  
  可以看到对象b改变了类变量的值
  对象a显示当前类变量的值也改变了
  
  所以可以得出一个结论
  类变量是类中的全局变量 这样是不是更加容易理解了?
  呵呵 别高兴 事实上类变量也是类对象的实例变量
  为什么?请看第二章 一切皆为对象
  
  ======================================
  II 一切皆为对象
  ======================================
  
  大家知道 123 是一个数字 那它是Fixnum(长整)类的对象
  而Fixnum(长整)类的超类(父类)是Integer(整数)类
  这些看过F1内部类的朋友 应该比较清楚
  首先我们先了解2个方法
  一个是Object类的class方法 也就是对象的方法 作用是返回他是什么类的对象
  另一个是Class类的superclass方法 也就是类的方法 作用是返回他的超类
  如果没有超类 则返回nil对象
  先举一些例子
  
  p "123".class 可以看到结果是 String
  p 123.class   可以看到结果是 Fixnum
  p [123].class 可以看到结果是 Array
  p /123/.class 可以看到结果是 Regexp
  
  这些结果是理所当然的 每个对象返回各自的类
  我们拿第一个返回的String类来追查下去
  
  p String.superclass
  
  显示结果 Object
  暂且知道String类的超类为 Object
  
  p Object.superclass
  
  显示结果为 nil
  说明 Object类 没有超类了 直接返回了对象nil
  那我们看对象nil属于什么类
  
  p nil.class
  
  显示结果 NilClass
  哈 nil对象还有个NilClass
  不管他看看NilClass的超类
  
  p NilClass.superclass
  
  显示结果 Object
  又是Object 不成循环了?
  Object的父类为nil,nil属于NilClass,而NilClass的父类又是Object
  这样的话是不是所有的东西(对象和类)最终的父类都是Object呢?
  可以在测试一个例子证实下 拿刚开始的 123 来测试
  
  p 123.class 结果为 Fixnum
  p Fixnum.superclass 结果为 Integer
  p Integer.superclass 结果为 Numeric
  p Numeric.superclass 结果为 Object
  
  = = 看来不管什么追踪到最后一定是Object
  接着又是 nil,NilClass,Object 循环
  说明循环到最后结果 NilClass 的父类也是Object
  这可以得出一个经典的结论 "一切皆为对象(Object)"
  既然 "一切皆为对象(Object)"
  那类也应该是对象 否则就不符合这句话了
  如果以上所说的理解了 我们接着进行第二波测试
  
  p Fixnum.class
  p Array.class
  p Regexp.class
  
  显示结果依次为 Class Class Class
  是不是觉得很惊讶 类居然也是对象
  天哪还存在一个Class类 这些内部类 在 Class类面前 只是它的一些对象
  之前怎么不知道还有这么一个类 那赶快继续追踪下去看看会有什么神奇的发现
  
  p Class.superclass
  
  显示结果为 Module
  = = 又冒出来一个 Module类 是 Class的超类
  不管他再继续
  
  p Module.superclass
  
  结果 Object
  看来没有悬念的,理论还是对的 追踪到底还是Object
  那继续我们的一切皆为对象的追踪
  在测试下面四个类对象 看看其所属
  
  p Object.class
  p NilClass.class
  p Class.class
  p Module.class
  
  显示结果仍然是四个 Class
  看来已经追踪到根了
  只能得出一个结论
  所有类对象都是 Class 类的对象
  综合以上2项测试 可以得出的结论
  所有类的超类都是Object 所有类对象都是Class类的对象
  记住这两句话 正是一切皆为对象的精华所在
  现在是否能解答第一章那个疑问:
  事实上类变量也是类对象的实例变量
  你明白了吗?
  明白了就好 你有能力去看第三章了
  因为 第三章非常难 = =
  
  ==========================================================
  III 类(Class),超类(superclass),对象(Object),模块(Module)
  ==========================================================

  第一节:对象
  
  上一章我们了解了ruby里一切皆为对象
  类既是类又是Class类的对象
  Object类里面提供了两个函数 object_id 和 __id__
  这两个函数的作用是一样的 其作用是返回对象的唯一标示
  暂时可以理解为 一个object_id 对应一个内存地址
  看一个例子
  
  [范例3.1]
  
  @a = Bitmap.new(1,1)
  3.times{p Bitmap.new(1,1).__id__}
  3.times{p @a.__id__}

  可以看到第1次输出结果 三个object_id均不一样
  第二次则完全相同
  由此得出的结论 Bitmap.new(1,1) 没有被赋值直接调用
  调用一次就分配一次object_id
  被赋值给@a后 调用3次@a.object_id 则是一个固定object_id
  虽然ruby会对无用对象调用GC回收垃圾
  但对对象的引用还是非常重要的 多引用少生成总是好的
  由此可以联想到效率问题

  可以再举个例子说明问题
  p "123".object_id
  不管多少次 结果都不一样
  每 p "123" 一次 就生成了一个 String 类的对象
  ruby中 每个对象在C中都对应一个 (结构体)struct
  String类的结构体中
  有定义了一个 char *ptr
  还有一个 long len
  第一个是字符串指针
  第二个是字符串长度

  ruby中当一个字符串对象生成 C就会定义一个字符串指针
  只想字符串存储的地址 开辟一段长度为len的内存空间
  用来存放字符串
  因为C里面字符串长度直接定义了long型,所以ruby里不用考虑int,long这些了
  有兴趣的朋友可以去参考ruby开源部分 这里就不贴出这些源代码了
  
  说完String类 接着
  
  也有两个特殊的类object_id是唯一的
  Fixnum 和 Symbol
  我们可以证实下
  
  3.times{p 123.__id__}
  3.times{p :123.__id__}
  
  object_id是一样的
  为什么呢
  其实这两个类特殊类无法生成对象的
  
  Fixnum.new
  Symbol.new
  
  报错!说没有new方法....
  p Fixnum.methods.include?("new")
  
  其本身在C没有结构体,就对应一个值(Value)
  
  具体没有new方法的类
  是因为删除了类方法new
  class Fixnum
    class << self
      undef :new
    end  
  end
  这是在虚拟类(元类)中删除了类方法new
  这段代码可能很多人就会有疑问了
  第二节会着重来解释
  
  第二节:类结构
  
  类大家应该最熟悉不过了
  可能很多人不知道真实的类结构
  第二章我们讲了ruby一切皆为对象
  举个简单例子
  
  a = Sprite.new
  p a
  
  p出来的结果是
  #<Sprite:0x130cf80>
  说明对象a属于Sprite类
  0x130cf80 则是对象名称地址
  
  再来看
  class Sprite
    p self
  end  
  
  结果显示 Sprite 它是一个类 很好理解
  
  class Sprite
    def show
      p self
    end
  end  
  Sprite.new.show
  
  结果是 #<Sprite:0x130cb78>
  是一个对象 因为是在对象方法的内部嘛
  也很好理解
  
  最后看2个东西
  先一下前面一章最后出现的那个东西
  class Fixnum
    class << self
      p self
    end  
  end
  
  显示结果 #<Class:Fixnum>
  
  赫赫 Fixnum是Class类的对象
  也就是类都是Class类的对象
  具体这些类对象的作用 就是在
  class << self
  end  
  这个区域中体现
  
  这个区域就是隐藏在类和对象背后的 类对象的区域
  此区域可以把类视为对象来进行操作
  被称之为 虚拟类(virtual class)或者元类(Meta Class)
  
  在看一个
  
  a = "123"
  class << a
    p self
  end  
  
  结果:
  #<Class:#<String:0x130cba8>>  
  
  这种表现形式很奇怪吧 是String类的对象属于Class类
  也有点违反常理吧
  因为一般情况下表现方法是 某对象直接属于某类
  如 "123"属于String类
  这样表现我们理解为 某对象形成的类
  这个
  class << a
  理解为 对象a形成的类 一般也是对象
  我们称之为元对象
  那既然是对象形成的类 那这个类中定义的东西
  一定属于这个对象 也是这个对象独有的
  如果要把它说成类 那他是一个单态类
  
  那到现在为止说了2个东西
  元类和元对象
  他们是客观存在的  
  
  现在我们能把类的真实地结构明朗化了
  1.对象空间 某个类的所有对象都可以访问
  2.类空间 类被定义的时候就形成的
  3.元类空间 隐藏于类空间和对象空间之后的
    也是类被定义的时候形成的
  4.元对象空间 类的某个对象独有的空间
  
  对元类和元对象操作 就是元编程了
  学会元编程是非常有用的
  可以构建一些底层的框架 模版
  大家一定知道知道
  attr_reader
  attr_accessor
  这两个方法
  如在一个类中
  attr_accessor :a
  那这个类会在其内部动态的定义两个方法
  
  def a
    return @a
  end  
  def a=(value)
    @a = value
  end  
  
  虽然不是很庞大 但也算是一个模板
  有了简单的 发掘强大的不就是靠大家的想象力了么?
  其就是元编程的结果
  所以 元编程是非常强大的
  
  大家都知道
  类有实例(对象)方法和类方法
  实例方法就不用多说了
  类方法最典型的就是 new
  
  如果大家以上80%听懂的话
  应该能理解
  一个类的类方法也是Class类的实例(对象)方法
  具体就不解释了 赫赫
  
  说道这里 如果不能理解的朋友也没关系
  可以先消化一下 再继续看下去
  如果兴趣还比较浓的话
  接下来的元编程的应用 一定能吸引你
  
  ==============
  第三节:元编程
  ==============
  前一章节讲到了类真实的结构 有4个空间
  那我们先来证明一下
  
  还记得第一章的范例1.1吗?
  
  class Abc
    @a = 0
    def view_var
      return @a
    end  
  end
  
  a = Abc.new
  p a.view_var
  
  @a 定义在类中
  所以@a属于类 并不属于对象
  所以对象方法中无法访问
  
  除了类中可以访问@a
  还有一个地方可以访问
  对 那就是类方法

  class Abc
    @a = 0
    p @a
    def view_var
      @a = 2
      return @a
    end
    def self.show
      return @a + 1
    end
  end
  
  p Abc.show
  p Abc.new.view_var
  结果显示  
  0
  1
  2
  类Abc中定义了两个同名的实例变量@a
  但是各自属于不同的空间 也不互相冲突
  类方法show中可以访问类空间的@a
  实例方法view_var可以访问实例空间的@a

  那类还有2个空间
  元类的空间 和 元对象的空间
  我把这个范例扩展下
  
  [范例3.2]

  class Abc
    # 这里是类空间
    @a = 0
    p @a
    def view_var
      # 对象方法内部是对象空间
      p @a        # 结果为nil 当前空间未定义
      @a = 2
      p @a        # 结果为 2 当前空间定义了
    end
    def self.show
      # 类方法内部也是类空间
      p @a + 1    # 结果为1 开始类空间定义了@a = 0
    end
    class << self
      #这里是元类空间
      p @a        # 结果为nil 当前空间未定义
      @a = 3
      p @a        # 结果为 3 当前空间定义了
    end  
    class << "abc"
      #这里是元对象空间
      p @a        # 结果为nil 当前空间未定义
      @a = 4
      p @a        # 结果为 4 当前空间定义了
    end  
  end
  Abc.show
  Abc.new.view_var
  
  测试下来 是否证实了类有4个空间?
  答案肯定的 它们相互之间是独立的
  每个空间的实例变量@a 作用域只在当前空间
  
  那类变量(@@开头)我们在第一章说过 是类中的全局变量
  那类中的4个空间都能访问到它 这里就不再举例了
  
  我们平时做游戏的时候可能都只是在对象空间中操作
  类空间也很少用到
  最典型的在类空间中用到的就是
  attr_accessor(*arg)
  这类类函数了
  
  这些概念清楚了 我们终于可以开始写一些应用了
  还是看范例
  
  [范例3.3-类方法的定义]
  
  class Abc
    def self.show
      p "abc"
    end  
  end
  Abc.show
  
  其实这种定义方法大多数人都知道
  self代表Abc类本身
  self.show表示定义类Abc的方法show
  也就是Abc的类方法show
  把def self.show 替换成
  def Abc.shwo 当然也是成立的
  
  还有一种定义类方法的方法
  是在自身的元类中定义
  
  class Abc
    class << self
      def show
        p "abc"
      end  
    end
  end  
  Abc.show
  
  可能有些人已经知道是为什么了
  元类是把类当作Class类的对象
  那在Class类中 也就直接用
  def show来定义类对象Abc的对象方法 show了
  当然 替换成 class << Abc 也是可以的
  是不是很容易理解了?
   
  [范例3.4-私有化对象方法]
   
  class Abc
    def show
      p "abc"
    end
    def pr
      show
    end  
    private :show
  end  
  
  Abc.new.pr
  Abc.new.show  

  第一个结果显示"abc"
  对象调用其方法pr,
  pr方法中再调用show方法 显示出最终结果
  
  第二个结果就报错了
  说被私有化的方法无法访问
  大家知道被私有化的方法只能在 方法内部调用
  无法被对象直接调用 那很显然这个例子就报错了
   
  那私有化类方法怎么实现的呢?
  
  [范例3.5-私有化类方法]

  我们来实现下私有化类方法new   
  
  class Abc
    class << self
      private :new
    end  
  end  
  
  Abc.new
  
  报错new方法被私有化了 也正是我们想要的结果
  但很容易就实现了
  有些已经知道的朋友可能已经不需要解释了
  但还是解释下吧 = =
  请一定要记住了 以后的范例可能很少解释这个了
  在Abc的元类中
  Abc被看作Class类的对象 私有化了对象Abc的方法 new
  其具体作用体现在
  Abc作为类 其类方法new被私有化了
  
  那有些朋友可能有疑问了 既然没有了new方法
  怎么创建对象呢?
  
  赫赫 很简单的答案 因为是私有化了类方法new
  可以自己定义一个 隐藏的new方法
  
  [范例3.5-隐藏的new方法]
  class Abc
    class << self
      private :new
      def hidden_new(*args,&blk)
        return new(*args,&blk)
      end  
    end  
    def show
      p "abc"
    end  
  end  
  a = Abc.hidden_new
  a.show
  
  Abc类被生成的时候
  在其元类中把类方法new私有化掉
  在自定义一个类方法hidden_new 用来返回原来的new方法
  赫赫 看起来是不是很巧妙?
  
  之前说到的那个范例是不是也很好理解了?
  为什么Fixnum类没有new方法呢?
  因为他在Class类内被删除了 而不是被私有化了
  
  class Fixnum
    class << self
      undef :new
    end  
  end
  
  [范例3.5-attr]
  class Abc
    attr_accessor :a
  end  
  
  o = Abc.new
  o.a = "123"
  p o.a
  
  当然创建一个类可以没有构造函数initialize
  可以直接用对象初始化,访问或修改属性

  如果attr在元类中 就自动创建了类函数
  class Abc
    class << self
      attr_accessor :a
    end  
  end  
  
  Abc.a = 9
  p Abc.a
  
  我们应该知道一个类的的对象创建的时候会自动
  运行函数initialize
  如果没有定义 那自然也就不会运行了
  所以initialize不是必须的
  
  反之 我们在创建的时候也可以不掉用构造函数
  Class类中还提供了一个函数allocate函数
  是用来创建一个空对象 其不掉用构造函数initialize
  仅分配对象内存
  
  [范例3.6-创建空对象]
  class Abc
    def initialize
      @a = 1
      p @a
    end  
    def show
      p "123"
    end  
  end
  
  a = Abc.allocate
  结果就显示并没有显示 1
  说明没有调用initialize函数
  调用对象方法还是一样
  a.show
  显示结果 "123"
  
  那a.initialize这样可以吗?
  测试结果说 initialize是私有化方法
  难道就没有办法了吗?
  有~用对象的send方法可以访问对象的私有化方法
  
  a.send :initialize
  这样就可以了
  
  那有了这方法就算new方法被undef了也不怕了
  我们可以用 allocate 方法来创建一个空对象
  然后用send来访问构造函数initialize
  一样达到了new方法的效果
  简单的说
  创建一个对象就是
  先为对象分配内存 然后再初始化函数
  new = allocate + initialize
  
  我们还可以根据不同情况来选择使用构造函数
  还可以用对象传递block的方法来初始化实例变量
  当然这不属于元编程了
  既然谈到了构造函数 就举2个例子吧
  可能不是很好的方法 仅锻炼思路
  
  [范例3.7-选择创建构造函数]
  
  class Abc
    def initialize(a,b,c,d)
      @a,@b,@c,@d = a,b,c,d
    end  
    def self.init
      return new(1,2,3,4)
    end  
    def self.init2
      return new(:a,:b,:c,:d)
    end
    def show
      p @a,@b,@c,@d
    end
  end
  
  a = Abc.init
  b = Abc.init2
  a.show
  b.show
  
  [范例3.8-block作为构造函数的参数]
  
  class Abc
    attr_accessor :a,:b,:c,:d
    def initialize(&blk)
      instance_eval &blk
    end
  end

  a = Abc.new do
    self.a = 0
    self.b = "abc"
    self.c = ["abc"]
    self.d = /abc/
  end  
  p a
  
  [范例3.9-元对象的单态方法]
  a = "123"
  class << a
    def times(n)
     p self * n
    end
  end
  a.times(3)
  
  结果是 "123123123"
  当然times是单态方法
  只属于对象a的方法
  
  [范例3.10-元对象的类方法别名]
  a = "abc"
  class << a
    p self.methods
    alias_method(:o_id,:object_id)
  end
  p a.object_id
  p a.o_id
  
  这样直接把object_id方法别名了
  并且成为对象a的单态方法
  
  [范例3.10-形成一个单态类]
  
  class Abc
    @ori_new = new
    class << self
      private :new
      def singl_new
        return @ori_new
      end  
    end
  end  
   
  先创建一个new的引用@ori_new
  然后再其元类中私有化new方法
  重新定义一个类方法 singl_new
  用来返回一个引用@ori_new
  这样创建的类就是一个单态类了
  
  好了通过这些例子大家对元类 元对象
  应该有比较深刻地了解了 更多的功能
  留给着大家去发掘
  
  ====================================
  第四节: 模块
  ====================================
  
  模块就比较简单了
  结构没有类那么复杂
  因为Module类是Class类的父类
  并没有Class类可以创建对象的功能
  所以我们认为它是静态的
  或者认为它本身就是一个单态类
  这也是它的一个特殊性
  因为这个特殊性我们可以把它作为一个容器
  或者一个封包 用来与类之间的通信
  inlcude 方法 就是将模块混入类中
  类可以调用模块的方法
  这叫做模块的混入技术(Mixin) 相信很多人都知道
  
  我们这里主要讨论一下模块的特殊性
  在此之前我们先解答一个范例
  
  [范例3.11]
  
  module Graphics
    def self.transition(*args)
      
    end  
  end

  class << Graphics
    def.transition(*args)
   
    end  
  end  
  
  第一种重写模块方法 大家应该熟悉
  必须方法名前+self
  第二种 用我们之前学到东西来分析下
  还记得任何类都是Class类的对象么?
  赫赫,Module类也不例外 那...
  class << Graphics
  被称为Graphics的元类
  可以解释为
  Graphics作为Class类对象
  来定义对象方法transition
  所以不需要 +self 了
  那再看一个范例来证实下
  
  [范例3.12]
  module Abc
    class << self
      def update
        p "abc"
      end
    end
  end

  class << Abc
    alias ori_update update
    def update
      ori_update
      p "def"
    end  
  end  
  
  Abc.update

  结果显示
  "abc"
  "def"
  
  看来是这么回事
  元类中 定义类方法的别名 也是用alias
  如果模块中定义模块方法的别名就不太一样
  我们先看如果创建一个模块方法

  [范例3.13]
  module Abc
    def self.show
      p "123"
    end  
  end
  
  ori = Abc.methods :show
  ori.call  

  我们把模块方法保存在变量ori里面
  成为了一个对象方法
  看上去有点像单态方法
  但却完全不一样 因为这个对象只是保存了一个方法
  属于Method类的对象
  所以并不是ori.show就可以执行
  那Method类中提供了一个 方法 call
  所以需要调用的时候用call方法来调用
  
  同样可以保存对象方法

  [范例3.14]
  class Abc
    def show
      p "123"
    end  
  end
  
  a = Abc.new
  b = a.method :show
  b.call
  
  只要记住 创建Method对象的方法
  对象.method :对象方法
  
  如果对象是类的话 比如上面那个例子Abc
  那后面的方法就是类方法(类作为对象)
  
  对Method方法有了了解 就很容易想到别名了
  还记得now_loading中的这句吗?
  原帖: http://rpg.blue/viewthread.php?tid=104649

  module Graphics
  @@ori = method("transition")
    def self.transition(*args)
      StartNowLondingTr.stop
      @@ori.call(*args)
    end  
  end  
  
  记得菜刀兄有过疑问
  现在就很好解释了
  用类变量@@ori生成一个Method对象
  其作用保存了原来的方法 transition
  然后重写方法 transition
  方法中用 @@ori.call(*args) 把原来的方法
  调用回来
  然后加入定义其他的东西
  
  感觉是不是跟 alias很像?呵呵
  
  Method类还有一个方法 ality 是用来返回方法参数的个数
  如果方法参数为不定参数形势(*args)
  返回值为-1
  
  具体作用在元编程里会很常用
  记得这句经典的报错吗?
  wrong number of arguments(x for x)
  表示参数不正确 后面的 x for x
  如果 0 for 1
  表示 原来方法有1各参数 调用的时候参数为0
  
  这就是一个很经典的元编程
  
  监测方法调用时候的参数
  如定义的时候为不定参数则 不检查调用时候的参数
  如不是不定参数
  调用时候的参数个数和定义的时候数量如果不正确
  就调用error程序

  模块还有一个特殊性 就是封装
  把一些方法 常量封装起来 可以和类进行通信
  
  最常用的方法就是
  include 和 extend
  
  [范例3.15]

  module A
    def show
      p "123"
    end  
  end
  
  class Abc
    include A
  end
  
  Abc.new.show
  结果
  "123"
  
  这种方法大家都知道
  把模块方法引入类中成为对象方法
  
  module A
    def show
      p "123"
    end  
  end
  
  class Abc
    extend A
  end
  
  Abc.show
  
  extend则是把模块方法变为类方法
  
  extend还有一种功能就是把
  模块方法成为某个对象得单态方法
  我们把对象作为单态类来把模块方法
  变为单态类的类方法
  
  [范例3.16]
  
  module A
    def show
      p "123"
    end  
  end
  
  class Abc
  end
  
  a = Abc.new
  a.extend(A)
  a.show
  
  b = Abc.new
  b.show
  
  显然 b.shwo 就报错了
  对象b没有方法show
  
  最后再说一个经常看到的模块函数
  module_function
  
  大家直到 模块中的实例函数
  因为模块没有对象的概念 就变成私有的了
  要在外部访问这些函数必须让其成为模块函数(类函数)
  一种方法就是函数名前加上self
  另一种方法就是使用模块函数 module_function
  使那些私有的函数变成模块函数
  
  [范例3.17]
  
  module Abc
    def pr
      p "pr"
    end  
    def pr2
      p "pr2"
    end  
    def self.show
      p "show"
    end  
    module_function :pr2
  end  
  
  Abc.show
  显示 "show" 因为show是模块函数 直接可以调用
  Abc.pr
  报错了 没有模块函数pr 因为pr是模块Abc的私有函数
  Abc.pr2
  显示 "pr2"
  因为 module_function :pr2 把 pr2 变为模块函数
  所以可以直接调用
  
  module_function 后面参数可以是多个
  如 module_function :a,:b
  同时把多个私有函数转为模块函数
  这点和 private :a,:b 很像
  如果 module_function 后面的参数名不写
  则 这行以下的私有函数 全部被转为模块函数
  
  记得之前说的模块函数的别名 用到了Method类的对象吗
  那模块中那些私有化的方法别名呢?
  其实根类中的实例方法别名差不多的
  类的实例方法别名用 alias
  模块私有方法别名用 alias_method
  
  [范例3.18]
  module Abc
    def show
      p "123"
    end  
  end  
  
  module Abc
    alias_method :ori,:show
    def show
      ori
      p "456"
    end
  end  
  
  class A
    include Abc
  end  
  
  A.new.show
  
  结果
  "123"
  "456"
  
  这章节终于讲完了(第五节是纯范例)
  相信大家对类的真实结构 模块的特殊性
  模块和类的关系 应该有更深一步的了解了
  其实本次教程最难得地方已经讲完了
  接下来几章 大家看起来可能就轻松一点了 赫赫
  
  ==============================
  第五节: 综合应用(不断更新范例)
  ==============================
  
  [范例5.1 attr的实现]
  
  class Class
    def new_attr_accessor(*args)
      args.each do |name|
        class_eval <<-METHODS
          def #{name}
            return @#{name}
          end
          def #{name}=(value)  
            @#{name} = value
          end   
        METHODS
      end   
    end  
  end  
  
  class Abc
    new_attr_accessor :a,:c
  end
  
  b = Abc.new
  b.a = /123/
  p b.a
  
  b.c = Sprite.new
  p b.c


  ==============================================
  IV:   字符串,符号,范围
  ==============================================
  
  创建字符串有很多方法
  比如
  a = "1"
  a = String.new("1")
  除了这两种 还有一种方法
  a = <<-STR
    1
  STR

  这种定义方法比较特别 被称为集成字符串
  
  在STR和STR之间 不需要加单引号('')和双引号("")
  如果加了 这些符号也会被认为是字符串
  
  虽然形式不同但都是String对象
  
  <<-STR 和 STR
  分别是字符串开始的标志 和 结尾的标志
  -号可以省略
  但是结尾标志必须写在行首
  赫赫 这和
  =begin =end代码段注释差不多
  不写在首行就报错了
  建议还是加上符号-
  
  接着说符号(Symbol)
  一个:开头的为符号
  大家知道前面说的两个类是无法生成对象的
  一个是Fixnum 另一个是Symbol
  这些是都是立即值 即直接返回一个值
  
  符号最常用的地方是用于参数传递
  我们经常看到 调用方法的时候用符号传递参数
  比如
  
  [范例 4.1]
  class Abc
    def initialize(a)
      case a
      when :car
       p "123"
      when "car"
       p "456"
      end  
    end  
  end
  Abc.new(:car)
  Abc.new("car")
  
  看上去用符号传递和用字符串传递差不多
  但是从效率角度上说 符号肯定比字符串效率高
  因为符号是立即值么
  
  学过rails框架的朋友一定知道
  rails框架就是用大量的符号传递参数
  ruby内部有些方法用符号传递
  和用字符串传递都是可以的
  如 attr_accessor :a
     attr_accessor "a"
  
  字符串和符号也可以互相转换
  "abc".to_sym  返回 :abc
  :abc.to_s     返回 "abc"
  
  有了这两个方法我们可以
  p "abc" + :abc.to_s
  结果肯定是 "abcabc"
  如果要转为符号还得 "abcabc".to_sym
  确实有点麻烦
  
  符号类本身没有定义过计算的方法
  如 :abc + :abc
  不可能返回 :abcabc
  
  我们可以自己动手写一个符号连接的扩展
  
  [范例 4.2]
  class Symbol
    def +(sym)
      return (self.to_s + sym.to_s).to_sym
    end  
  end
  
  p :abc + :abc
  
  这个扩展很简单而且很实用吧 符号其实就这点作用
  
  最后再说下范围
  范围也是一个类 Range
  就可以用new来创建
  也可以用一个变量来保存这个对象
  比如
  
  a = Range.new(1,9,false)
  for i in a
    p i
  end   
  for i in 1..9
    p i
  end
   p a.to_a
  
  这两种方法是一样的
  创建范围对象的时候
  第三个参数是确定是不是包含最后一个元素
  如果不写 则默认为false,包含最后一个元素
  to_a方法 可以将范围对象转换为数组对象
  
  范围还可以用英文字母
  也可以反向表示
  
  如 "a".."z" ,"Z".."A", 0..-100
  
  范围也有 include? 方法
  用于判断是否在范围内
  
  p ("a".."z").include? "x" 返回true
  
  关于这一章节就说到这里
  
  =====================================
  V:    方法 , proc
  =====================================
  ===========
  第一节 方法
  ===========
  方法有实例方法和类方法
  实例方法定义 def + 方法名
  类方法定义   def + self. + 方法名
  方法名可以用以下几种符号结为
  ?
  !
  =
  这几种应该大家都接触过
  ?号结尾一般用于返回一个是(true)或否(false)的结果
  我们自己来写一个判断对象是否为字符串的方法
  
  [范例 5.1]
  class Object
    def a_string?
      return self.kind_of?(String)
    end  
  end   
  
  p "123".a_string?  结果 true
  p 123.a_string?    结果 false
  
  
  !号结尾一般用于返回修改过的结果 其本身的引用也会被修改
  带一定的危险性  默认有gsub 和 gsub!
  我们来看下区别

  a = "aaaaa"
  p a.gsub(/a/){"b"}
  p a
  
  p a.gsub!(/a/){"b"}
  p a
  

  =号结尾是为方法预定赋值 方法返回的时候就是这个值
  如:
  [范例 5.3]
  class Abc
    def abc=(a)
      c = 0
      b = 100
      return 99
    end  
  end
  p Abc.new.abc=("123")

  返回结果为 "123"
  虽然方法内有 return 99 但是返回的结果却是 "123"
  因为调用方法的时候=("123")已经为方法确定了返回的结果
  
  接着说 方法的参数
  方法的参数 可以是任意的对象
  也可以是一个方法 他会执行一次方法然后把返回值传入参数
  如 123,"123","123"[0]="1",Sprite.new
  
  [范例 5.4]
  class Abc
    def show(a,b)
      p a
      p b
    end  
  end
  Abc.new.show("abc"[1]="x",Sprite.new)

  结果显示 "x" 和 一个Sprite对象
  
  这种参数是已经把参数的数量确定了
  如不确定参数数量的情况下
  我们可以用不定参数(*args)
  带*号 表示把参数全部放入数组args中
  看范例
  
  [范例 5.4]
  class Abc
    def show(*args)
      p args
    end  
  end
  Abc.new.show(1,"abc",/abc/,:x)
  Abc.new.show("a","b","c")
  
  结果
  [1,"abc",/abc/,:x]
  ["a","b","c"]

  还可以只确定其中几个参数 剩下的为不定参数
  如
  
  [范例 5.5]
  class Abc
    def show(s1,s2,*args)
      p s1
      p s2
      p args
    end  
  end
  Abc.new.show(1,"abc",/abc/,:x)
  Abc.new.show("a","b")
  
  结果大家测试下应该知道了
  我们看第二个结果
  如果确定2个参数 还有一个不定参数
  那如果只带2各参数 不定参数就是一个空数组[]
  如果确定2个 调用的时候只有1个参数
  那么还是会发生经典的参数数量不正确的error
  
  最后要说的是数组的元素展开作为参数
  
  [范例 5.6]
  class Abc
    def show(s1,s2,*args)
      p s1
      p s2
      p args
    end  
  end
  
  a = [1,2,3,4,5]
  p Abc.new.show(*a)
  
  结果显示
  1
  2
  [3,4,5]
  
  看来数组的元素全部被作为参数了
  这是一种经常用到的方法

  ===========
  第二节 proc
  ===========
作者: 水蓝    时间: 2008-11-1 07:32
提示: 作者被禁止或删除 内容自动屏蔽
作者: 美兽    时间: 2008-11-1 18:29
强烈期待,RUBY有不少郁结的地方,希望能找到答案。
作者: 亿万星辰    时间: 2008-11-1 18:42
强文,重点关注{/fd}
作者: 灼眼的夏娜    时间: 2008-11-1 18:43
{/hx}偶来给秀秀踩帖来了,踩完下班闪人= =

发现美兽sama- -b
作者: cmbljsw    时间: 2008-11-1 23:26
关注ing……
话说,很多复杂类的都米人发,在RM里又找不到说明……
作者: 美兽    时间: 2008-11-2 03:01
以下引用灼眼的夏娜于2008-11-1 10:43:36的发言:

偶来给秀秀踩帖来了,踩完下班闪人= =

发现美兽sama- -b



你到上海可真会找地方住,把那家伙吓的不敢出门…………

作者: 猫哥哥    时间: 2008-11-6 02:12
研究得很透彻啊,期待能够顺利完成,踩帖留名{/cy}
作者: 美兽    时间: 2008-11-6 02:38
太精彩了,
边测试边吸收,刚刚将第三节看完,四个空间的类比极为新鲜,茅塞顿开,我在其他地方从未见到,接触到很多新的概念,说句实话,虽然查过相关资料,但从未想出元编程的具体应用,第四节暂时停止,需要时间吸收第三节的内容。

强烈期待下文,另外LZ是否可以多举一些元编程实际应用的例子。
作者: hide秀    时间: 2008-11-6 16:09
添加一个元编程的具体应用 写第五节了 以后可以不断更新 {/wx}
作者: baby66    时间: 2008-11-23 23:04
{/dy} 能不能用点c语言的语法串通下啊 我看着有些概念就和c里和像但用时间不多 很多地方也说不上哪里就是c里的东西,那样更容易接受 哪些是ruby特殊的 也就是和c、java这些不同的对比着讲更能更快上手 {/gg}
作者: 威廉华莱士    时间: 2008-12-1 22:17
不错~~~~基本能看懂~~~`研究的太细了~~~~`元编程和元类、元对象这几个概念在之前还从来没接触过的说~~~~~长知识了....元编程我想这个最大的用途在于拓展和维护系统框架上吧.....还有一个问题问下~~下面范例的那个block是指什么来着?

[范例3.8-block作为构造函数的参数]

class Abc
   attr_accessor :a,:b,:c,:d
   def initialize(&blk)
     instance_eval &blk
   end
end

a = Abc.new do
   self.a = 0
   self.b = "abc"
   self.c = ["abc"]
   self.d = /abc/
end  
p a


继续期待~~~
作者: 灼眼的夏娜    时间: 2008-12-1 23:13
block 指定就是

do
  self.a = 0
  self.b = "abc"
  self.c = ["abc"]
  self.d = /abc/
end  

这段,即传给参数的blk
作者: tranpol    时间: 2008-12-3 20:38
强……就一个字
俺存回家慢慢研究去
好东东啊!!!
作者: 星月无痕    时间: 2008-12-4 14:11
提示: 作者被禁止或删除 内容自动屏蔽
作者: 周子民    时间: 2008-12-27 02:35
提示: 作者被禁止或删除 内容自动屏蔽
作者: 50568358    时间: 2009-1-1 20:51
看能不能学会
作者: 465889216    时间: 2009-1-20 10:07
提示: 作者被禁止或删除 内容自动屏蔽
作者: dbshy    时间: 2009-1-21 18:34
前辈,快更新{/hx}
作者: hide秀    时间: 2009-1-22 17:05
前阵子忙于TITLE参赛....竟然忘了还有这教程没更新。。。某居然开始不负责任了{/ll}
年后吧......无奈。。。过年7天都有安排了。。。

作者: yangff    时间: 2009-2-6 05:05
我恨对象!!!
台麻烦了。
害得我。。。。
(不是Ruby是DotNet拉)
作者: 七仙女    时间: 2009-3-31 07:11
提示: 作者被禁止或删除 内容自动屏蔽
作者: 云天袁    时间: 2009-4-6 22:34
提示: 作者被禁止或删除 内容自动屏蔽
作者: OCTSJimmy    时间: 2009-5-23 23:56
线程,正则表达式……
期待下……
作者: dbshy    时间: 2009-5-31 21:35
秀秀快来更新吧.......
作者: 缘一剑    时间: 2009-6-13 01:45
感谢LZ这么用心..
作者: faust    时间: 2009-7-26 09:35
这个不错~~
作者: 林子    时间: 2009-7-26 14:39
本帖最后由 林子 于 2009-7-26 15:00 编辑

刚来6R就看到HIDE sama...
纠结ING...“永远说再见了秀秀......”TOT
再见..原来是再也不见吗...
"また 春に会いましょう"
秀的话我们应该相信.
嗯...总有一天能再见到吧..
在那之前只要安静的等待就好...
作者: gdmingxuezhi    时间: 2009-8-11 10:40
提示: 作者被禁止或删除 内容自动屏蔽
作者: 神龙出世    时间: 2009-8-19 13:55
很好,顶个,lz不容易啊
作者: huazi922    时间: 2009-10-4 14:21
我什么时候能仔细看懂啊
作者: a554187203    时间: 2010-6-26 16:41
看不懂~我哭!英语没有学好~
作者: kobeonetow    时间: 2010-10-30 01:01
great job! well done...  I am a beginner hopefully this will help..thanks so much
作者: summer92    时间: 2011-5-16 12:11
- -高人出现,能否讲讲 ruby里的字符串处理啊,abc0.xxx=abc 这样的,正则不喜欢用....
abc0使用 xxx 方法处理后能得出 abc这个字符串
作者: godwoow    时间: 2011-5-22 00:20
提示: 作者被禁止或删除 内容自动屏蔽
作者: naibo456    时间: 2011-8-14 21:16
快继续更新啊~期待中……
作者: 1343001608    时间: 2011-9-2 19:50
本人是新人有些看不懂以后请多指教!
作者: 鼎风乱影    时间: 2011-9-18 09:05
…………我眼都花了,还是视频好:)
作者: qwertqq2013    时间: 2014-1-23 15:32
能够结合制作游戏实战练习脚本就更好了
作者: 千昭    时间: 2014-1-31 19:23
很好的教程
细节决定成败

PS:可否稍微美化下排版
作者: 失格病房    时间: 2014-8-4 18:07
踩帖留名~




欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1