设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
楼主: DeathKing
打印 上一主题 下一主题

[讨论] Ruby/RGSS Tips 每日一更 [技术区的版聊帖?]

  [复制链接]

Lv1.梦旅人

梦石
0
星屑
110
在线时间
953 小时
注册时间
2007-4-25
帖子
805
72
发表于 2011-1-16 04:09:30 | 只看该作者
你可能听说过“解释性语言”这个词,它意味着一种间接被软件解释器程序执行(解释)的语言;与之相对的是“编译性语言”,即可以直接被翻译为机器码并交给 CPU 执行的语言(实际上,CPU 也可以被看作一种机器指令的解释器,只不过是硬件集成的)。然而,无论是“解释”或是“编译”,都并非是语言本质上的特性,而只是实现语言的方式。理论上来说,任何语言都同时可以被编译和解释。所以,“解释性语言”、“编译性语言”在实践时通常是指“频繁被解释的语言”、“频繁被编译的语言”。

早期的程序语言,要么就是被编译,要么就是被解释,所以他们只需要编译器或解释器的其中一个。比如,Shell 脚本就总是由命令行解释器(软件)来执行,而 C 程序总是由 C 编译器一次性翻译为机器码。随着计算机科学的不断发展,这种单调性也逐渐被淘汰,出现了很多使用混合模式的语言实现——同时拥有解释性和编译性。

官方的 Ruby 实现(CRuby)在 1.9 版本之前还没有一个虚拟机,所以它只有一个被称为 MRI(Matz’s Ruby Interpreter)的解释器。MRI 是一个纯解释性质的 AST walker(AST = Abstract Syntax Tree),硬直翻译成中文就是“抽象语法树步行器”,意即是在表示了整个程序的抽象语法树上行走并求值(之所以说“抽象”,是因为这个语法树并不一定非得有一个物理的树结构)。

到了 CRuby 1.9 版本时,出现了 YARV 这个 Ruby 虚拟机,“解释性”再也不是 CRuby 的唯一特性。在执行 Ruby 程序时,YARV 会首先将 Ruby 代码编译为 YARV 虚拟机器指令,这是一种中间代码,和 JVM 的字节码,CLR 的 托管代码类似。之后,YARV 再在这一系列 YARV 指令上进行动态的解释。所以 YARV 即是编译器,也是解释器。它进行了一种被称为 预编译(AOT,Ahead of Time Compilation)的处理,使得优化代码的空间得到了提升。

同理,同样是基于虚拟机之上的 JRuby 和 IronRuby 等实现自然也都会先对代码进行编译,产生一种中间代码。当然,他们还有别的模式,比如下文提到的 JIT 编译。

为了进一步强调第一段黑体文字所描述的事实,这里再举两个例子:

C 语言通常被描述为“编译性语言”,但实际上只是“频繁被编译的语言”。目前已经存在很多 C 解释器以及 C 虚拟机,能够让 C 被解释执行,或混合编译和解释执行。两个个比较有名的是 CINT 和 Ch。

Sun 的 Java 实现更是典型的混合模式实现。它的虚拟机(JVM)发展至今,已然十分健全,除了预编译器以外,还有即时编译器(JIT,Just-in-Time Compiler),充分应用了 LLVM 结构。即时编译机制会进行运行时程序概要分析,巧妙地找到所谓的被经常执行的被称为“热点”(hot-spot)的一段指令,在运行时重编译为本地机器码,并让 CPU 静态执行。基于 JVM 之上的 JRuby 也早已支持 JIT 了。

点评

研究 RMVX 所发展出来的新语言开发的志向,可以一试撒..  发表于 2011-1-16 14:06
[email protected]:~> repeat 1 fortune
Matz is nice, so we are nice.
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
61
在线时间
24 小时
注册时间
2008-8-5
帖子
1924
71
发表于 2011-1-15 13:15:22 | 只看该作者
case 语句可以用来做对象运行时类型的分歧:

  1. #coding: GBK

  2. foo = rand >= 0.5 ? 'foo' : :foo

  3. case foo
  4. when String
  5.     p '字符串'
  6. when Symbol
  7.     p '符号'
  8. end
复制代码
但经过测试后,你会发现 `foo == String || foo == Symbol' 从来都是 false。实际上,case 在判断相等性时使用的是 `===',而不是 `=='。如果并不了解这个事实,你可能会在使用 case 的时候习惯性、显式地去引用 `foo' 的类型:

  1. case foo.class
  2. when String
  3.     # ...
  4. when Symbol
  5.     # ...   
  6. end
复制代码
接着你就会发现,这段 case 没有产生预期的效果。
这是因为 Class 覆盖了基类 Object#=== 方法,而 Class#===(obj) 返回的是 `self == obj.class'。case 语句会让 when 后面的对象成为接收者,并发送调用 `===' 方法的消息给它,类似 `String === foo' 的形式。这个表达式展开其实就是 `String == foo.class',所以起到了预期的判断效果。正因如此,`String === String' 这样的表达式反而会返回 false。,而这和在 case 后面引用 foo 的类型是一个道理,因为 foo.class = { String, Symbols }。

点评

Object#=== 应是直接返回 == 的结果;Class#=== 上面说了,判断 self == obj.class;<=> 是承自 Perl 的比较运算符,左边大、相等、右边大分别返回 -1,0,1  发表于 2011-1-18 08:28
判断的是值还是什么的说? <=> 这个呢??  发表于 2011-1-16 16:44
飞船运算符是 `<=>' 嘛 >_<  发表于 2011-1-16 10:34
【飞船运算符】  发表于 2011-1-16 08:58
专门用于 case 时的相等性判断 OvO  发表于 2011-1-16 02:45

评分

参与人数 1星屑 +132 收起 理由
zh99998 + 132 辛苦了o.o

查看全部评分

回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
110
在线时间
953 小时
注册时间
2007-4-25
帖子
805
70
发表于 2011-1-9 02:37:57 | 只看该作者
本帖最后由 苏小脉 于 2011-1-9 02:48 编辑

Ruby 和大部分 OO 语言一样,都是在语言、语法层仅支持单一方法分派的语言。单一分派(single dispatch)是在通过迟(动态)绑定实现多态时的一种思路,它依靠接收者的运行时类型动态地分派应该调用的方法。

Smalltalk、Python 以及 Ruby 使用的都是一种实现方式,那就是通过方法分派函数(dispatch function)和分派表(dispatch table)进行消息到方法的映射。每个类型在定义时就内置了这样的分派函数和分派表,后者是一个消息(符号)到实际方法的映射表,而前者接受一个输入符号,然后通过分派表映射到正确的方法(其间包含处理派生类继承的方法和模块 mixin 后的方法等比较复杂的过程)。当我们在 Ruby 中以 obj.method 的形式调用一个方法时,按照 Smalltalk 的程序语言概念,就是向 obj 这个对象发送了一个消息,传递了一个 :method 符号,而作为接收者的 obj 会通过分派函数在分派表中找到对应的方法。这整个过程就是所谓的动态方法分派(dynamic method dispatch)。

比如:基类和派生类有同名方法,但在实际调用时,随着接收者的不同,实际调用的方法也不同,当接收者是基类实例时调用基类实例方法,当接收者是派生类实例时调用派生类实力方法。关于这个的更多信息,还可以参考本主题早期的一篇关于迟绑定的回帖。

既然 Ruby 使用的分派模式唤作单一分派,那自然还有多重分派(multiple dispatch)。Common-Lisp 是一个众所周知的最早支持多重分派的语言。多重分派通常是指在动态判断接收者类型的同时也判断方法参数的动态类型的分派模式。更有甚者,会判断参数的值,返回类型,或返回值。

要注意这里是方法参数的动态类型(运行时类型),而不是静态类型,这是关键。对于支持静态类型的语言来说,由于方法参数的类型可以在编译时决定,所以他完全可以做静态分派,也就是静态绑定(早绑定)。静态类型语言中的方法重载就是这样的一个概念——静态类型的参数在编译时就可以确定,所以方法的分派也是静态的。

比如,C++ 的方法重载,就是通过不同的静态类型的参数实现的,这个过程不属于动态分派,自然也就不能称之为多重分派。实际上,大多数 C++ 编译器都会给重载的方法/函数进行一个所谓的方法名/函数名装饰(function name decoration)的处理,所以在链接的时候,实际参与链接使用的正式函数名并不是你在源代码中敲下的函数名,而会有一些别的奇怪的符号,这个想必自己写过 C++ 共享库的朋友都深有体会。动态多重分派的严格定义,是指方法名不变,根据接收者和参数类型(甚至参数值、返回类型、返回值)判断应该调用的方法的一种多态。当然,由于 C++ 本身有虚函数的机制,所以还是支持动态分派的,只不过不支持多重分派罢了。

像后来的 Java 这样的静态类型语言,看起来也是支持方法重载,实际上也和 C++ 一样,只是单一分派罢了。C# 是个怪胎,C# 4.0 同时支持静态类型和动态类型,这使得多重分派在 4.0 中得到了支持。Python、Ruby 都是没有静态类型的语言,所以不可能像 C++ 那样去根据静态类型在编译时做函数名装饰,也就不可能有静态分派。因此,传统意义上的方法重载在这样的纯动态类型语言中是不存在的。由于不能重载,相同名称的方法就只有一个定义,所以在语言层、语法层是不能实现多重分派的。要想在仅支持单一分派的语言中使用多重分派,只能在高层模拟进行,比如 Ruby 可以通过 Object#class 去判断对象的运行时类(型),然后根据类型的不同分别处理。很多第三方的扩展库就是这么做的。

  1. def foo(bar)
  2.         case bar
  3.         when String
  4.                 p 'bar'
  5.         when Symbol
  6.                 p :bar
  7.         end
  8. end

  9. foo :foo
  10. foo 'foo'
复制代码
输出:
:bar
"bar"
[email protected]:~> repeat 1 fortune
Matz is nice, so we are nice.
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
61
在线时间
24 小时
注册时间
2008-8-5
帖子
1924
69
发表于 2011-1-8 14:56:55 | 只看该作者
Symbols 和 Fixnum 类型的实例是不能拥有单例类和单例方法的。

  1. foo = 0
  2. def foo.bar; end # => can't define singleton method "bar" for Fixnum (TypeError)
复制代码
至于有这个限制的原因,窃以为是由于直接值和单例方法的实现导致的——
创建单例方法有一个先决条件,就是必须要先有一个单例类对象,之后才能在这个类上下文中进行方法定义。普通的对象实例都可以并默认拥有自己的单例类对象,然而 Fixnum 和 Symbols 有两个特性:可以有多个实例,且每个实例都是直接值。他们由 object_id 抽象地表示着,并不是物理存在于 Ruby 层的对象,所以并没有一个高效的方法来为每一个“抽象的”对象定义单例类。TrueClass、FalseClass、NilClass 类型则完全不同,虽然他们的实例引用也是直接值,但由于他们各自在整个程序的生命周期中都只有一个实例,所以他们的单例类就可以是这三个类本身。

  1. p true.singleton_class
  2. p false.singleton_class
  3. p nil.singleton_class

  4. obj = Object.new
  5. p obj
  6. p obj.singleton_class
复制代码
输出:
TrueClass
FalseClass
NilClass
#<Object:0x471c18>
#<Class:#<Object:0x471c18>>

而:
  1. p (:foo).singleton_class
复制代码
则:
in `singleton_class': can't define singleton (TypeError)

点评

所以很早以前还幻想实现++a或者a++之类的事情。  发表于 2011-1-8 15:39

评分

参与人数 1星屑 +132 收起 理由
zh99998 + 132 那啥,突出贡献奖,乃已经可以申请产地了,.

查看全部评分

回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
110
在线时间
953 小时
注册时间
2007-4-25
帖子
805
68
发表于 2010-12-27 00:08:22 | 只看该作者
Ruby 1.8 的 Kernel#require 可以接受一个绝对路径,如果不是绝对路径,就会在 $: 保存的搜索路径中进行搜索,但这默认不包括当前工作路径。

Ruby 1.9 多了一个 Kernel#require_relative,接受一个相对于当前源文件的路径,这样就不用显示地去把当前工作路径添加到 $: 中或显示地指定 `.' 了。要注意的是在 irb 中这个函数是无法使用的,因为 irb 并没有一个明确的源文件定义。

点评

老板真勤快……双蛋活动不参加么?  发表于 2010-12-27 00:13
[email protected]:~> repeat 1 fortune
Matz is nice, so we are nice.
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
61
在线时间
24 小时
注册时间
2008-8-5
帖子
1924
67
发表于 2010-12-26 00:14:10 | 只看该作者
想在第一时间获得 Ruby 官方实现的最新 tag 么,从官方的 SVN checkout 是不二的选择!

trunk
      $ svn co http://svn.ruby-lang.org/repos/ruby/trunk ruby
ruby_1_8 branch
      $ svn co http://svn.ruby-lang.org/repos/ruby/branches/ruby_1_8
ruby_1_8_5 branch
      $ svn co http://svn.ruby-lang.org/repos/ruby/branches/ruby_1_8_5

主干上的是 1.9.2 的开发,另外还有 1.8 两个版本的分支。如果你只想浏览,不想 checkout,可以用这个 ViewVC 的接口:
http://svn.ruby-lang.org/cgi-bin/viewvc.cgi/?root=ruby
没事可以读一读 Ruby 源代码什么的。
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
110
在线时间
953 小时
注册时间
2007-4-25
帖子
805
66
发表于 2010-12-25 23:56:38 | 只看该作者
Ruby 1.9.2-p136 今天刚发布了,这是 Ruby 1.9.2 的第二个发行版,它修复了很多已知的 1.9.2-p0 的 BUG。

Change log:http://svn.ruby-lang.org/repos/ruby/tags/v1_9_2_136/ChangeLog
下载:
http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.2-p...
  SIZE:   8819324 bytes
  MD5:    52958d35d1b437f5d9d225690de94c13
  SHA256:
33092509aad118f07f0483a3db1d4c5adaccf4bb0324cd43f44e3bd3dd1858cb

* http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.2-p136.tar.gz
  SIZE:   11155066 bytes
  MD5:    6e17b200b907244478582b7d06cd512e
  SHA256:
c4314df44f3ab81230685fb51c296ce21034f4c719e2fcc0baba221d19f28746

* http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.2-p136.zip
  SIZE:   12566581 bytes
  MD5:    f400021058e886786ded510a9f45b2c6
  SHA256:
84ffc047b29032ba848dbbf50d3302de7ac732db1448e57303c27ad4b47c2c5b


http://www.ruby-forum.com/topic/732814#new

点评

可以试试,我对 Rails 没认知……  发表于 2010-12-26 09:58
p136能支持Rails3么?  发表于 2010-12-26 09:17
[email protected]:~> repeat 1 fortune
Matz is nice, so we are nice.
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
61
在线时间
24 小时
注册时间
2008-8-5
帖子
1924
65
发表于 2010-12-24 03:57:05 | 只看该作者
正则表达式字面值的对象构建

  之前曾谈到了字符串扣留的概念,那么对于 Ruby 的正则表达式 Regexp 类型来说,是否也有类似的常量池机制呢?

  (注:以下输出的具体数字随环境而变,重要的是其前后的相等性)

  我们可以做一个简单的测试:

  1. 2.times { p "foo".object_id }
  2. 2.times { p :foo.object_id }
  3. 2.times { p /foo/.object_id }
复制代码
  输出:
5712492
5712432
146408
146408
5713608
5713608

  根据输出结果,我们似乎可以得出结论, Regexp 类型也有类似字符串扣留的机制。然而细心的人会进一步测试这段代码:

  1. 2.times { p Regexp.new(/foo/).object_id }
复制代码
  输出了两个不同的数字。这和之前的测试难道不是矛盾的吗?当然不是,这实际上和 Ruby 的语法分析器有关。

  Ruby 支持正则表达式字面值(/…/)。对于官方的 Ruby 实现来说,其语法分析器会在接受一个正则表达式的单词(token)时生成该正则表达式对象。也就是说,字面值形式的正则表达式是在语法分析时生成的,也可以宏观地看作是在编译时生成的。因此,在运行时,虽然会多次调用 /foo/.object_id,但由于语法分析器只看到了一个正则表达式字面值,其接收者永远都是一个对象。

  证明:

  1. ObjectSpace.each_object(Regexp) { |r| p r }
  2. /foo/
复制代码
  第一行是打印出当前的 Ruby 实例中所有 Regexp 对象,而这时解释器还没执行到第二行的字面值。实际的输出中,也确实包含 /foo/ 这个对象。

  如果语法分析器看到两个正则表达式字面值,自然也就会生成两个 Regexp 对象了:

  1. p /foo/.object_id, /foo/.object_id
复制代码
  输出:
5713896
5713812


http://szsu.wordpress.com/2010/12/22/regex_literal/
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
110
在线时间
953 小时
注册时间
2007-4-25
帖子
805
64
发表于 2010-12-23 06:33:26 | 只看该作者
本帖最后由 苏小脉 于 2010-12-24 03:56 编辑

字符串扣留

 先从一个 Ruby 实例看起:

p "hello".object_id, "hello".object_id

  object_id 可以获取一个对象的唯一标识,而这行代码打印出了不同的 ID,说明两次引用的字符串字面值生成了两个不同的字符串对象,但它们的内容是一致的。这在面向对象语言中是一个普遍的现象,在某些场合下可能会引起以下问题:

   1. 当频繁使用 Ruby 的字面值指定某个字符串时,每次通过 Ruby 的字面值指定的字符串都会生成一个新的对象,长此以往就会产生大量的内容相同的字符串对象。这种空间的浪费意味着不必要的数据占用了更多的 Cache 和内存,减少了其它资源利用 Cache 的机会,也同时增加了内存分配和回收的复杂度;
   2. 我们经常需要比较字符串是否相等(如散列表的键值比较),而无论是什么语言,底层的实现方法也无非是调用 memcmp、strcmp 之类的函数来比较内存,理论上这个过程需要 O(n) 的时间(n = 字符串长度),而如果这样的比较需要频繁进行,那代价也是不容忽视的。

  当今不少高级语言中,都内置一种被称为“扣留”(Intern)的机制,它解决了这一难题。它的思想是:使用一个全局的字符串常量池来管理字符串,每次准备分配新的字符串对象之前,程序都会先检查内容相同的字符串对象是否已经被“扣留”在了常量池中,若是,则不分配新的对象,直接引用常量池中的对象;若不是则分配新的对象,并“扣留”在常量池中。这当然增加了字符串对象构造时的时间,但却解决了上述两种情况下的空间(没有产生字符串的克隆)和时间(逐字节比较字符串变为了指针相等的判断)效率问题。

  这个机制最早由 Lisp 用来管理符号,后来也被 Python(intern(string))、Java(String#intern)、.NET(String.intern) 等语言继承,用来实现字符串的扣留。Ruby 通过符号表实现了符号对象的扣留机制,但内置的字符串对象并没有扣留。Ruby 的 String#intern 方法看似扣留,实则是把一个已有的字符串对象转换为符号对象。使用这个方法并不会节省内存,只能起到简化字符串比较的时间复杂度的作用。所以,在 Ruby 中,操作常量的字符串并处于上述两种情况时应该尽量使用符号而不是字符串。Ruby 1.9 的新 Hash 字面值支持一种更简洁的符号键语法糖,其设计目的就是为了推荐用户使用符号而不是字符串。

http://szsu.wordpress.com/2010/09/21/string_intern/

点评

立即值概念不同,立即值并不是真正物理存在的 Ruby 对象,只存在于 Ruby 逻辑层面上。相同的是这些值和符号一样都是常量,是不可变的(Immutable)。  发表于 2010-12-27 00:58
貌似某书上写符号和整数为【立即值】,特性之一为不能定义单例方法,看起来好像和这个扣留机制有点关系么  发表于 2010-12-27 00:23
Java 也有这样的机制的说. 以前有一次写 Java 程序的时候就犯过这类的错误. 两个不相等的同字符串~  发表于 2010-12-23 09:07
[email protected]:~> repeat 1 fortune
Matz is nice, so we are nice.
回复 支持 反对

使用道具 举报

Lv3.寻梦者

梦石
0
星屑
1195
在线时间
1564 小时
注册时间
2008-7-30
帖子
4418

贵宾

63
 楼主| 发表于 2010-11-28 13:17:10 | 只看该作者
( Dave Thomas && Matz ) @ Ruby Conf 2010








DeathKing于2010-12-11 17:30补充以下内容:
给新手的String类参考手册:http://rpg.blue/thread-162871-1-1.html

See FScript Here:https://github.com/DeathKing/fscript
潜心编写URG3中。
所有对URG3的疑问和勘误或者建议,请移步至发布页面。
欢迎萌妹纸催更
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-12-4 02:51

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表