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

Project1

 找回密码
 注册会员
搜索
12
返回列表 发新帖
楼主: DeathKing
打印 上一主题 下一主题

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

  [复制链接]

Lv1.梦旅人

梦石
0
星屑
110
在线时间
953 小时
注册时间
2007-4-25
帖子
805
6
发表于 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
星屑
110
在线时间
953 小时
注册时间
2007-4-25
帖子
805
5
发表于 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
星屑
110
在线时间
953 小时
注册时间
2007-4-25
帖子
805
4
发表于 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
星屑
110
在线时间
953 小时
注册时间
2007-4-25
帖子
805
3
发表于 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
星屑
110
在线时间
953 小时
注册时间
2007-4-25
帖子
805
2
发表于 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.
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
110
在线时间
953 小时
注册时间
2007-4-25
帖子
805
1
发表于 2010-11-18 06:56:30 | 显示全部楼层
本帖最后由 苏小脉 于 2010-11-18 06:57 编辑

The great ruby shootout:
http://programmingzen.com/2010/0 ... shootout-july-2010/

这是一个由来自 IBM 的软件工程师 Antonio Cangiano 执行的 benchmark,其结果展示了不同 Ruby 的实现在 Linux 和 Windows 上的不同性能。

官方版的 Ruby 实现(CRuby)开发环境是 Linux,优化时针对 Linux 的多,针对 Windows NT 的少,再加上编译器优化性能的差异(GCC <=> MSVC),使得 Linux 下的 CRuby 性能整体高于 Windows。

在 Linux 下,平均最快的实现似乎是 基于 YARV 虚拟机的 Ruby 1.9.2(即 CRuby 的最新稳定 Release),基于 Sun JVM 的 JRuby 紧随其后;Windows 下,JRuby 的性能胜过了 Ruby 1.9.2。

MagLev 和 Rubinius 是这前两名巨头最大的竞争对手,现在仍在快速发展。特别值得一提的是 Rubinius,其最新的 1.1 版本在 JIT 优化和 GIL 性能上又有了提升,相信她在下一次 shootout 会有杰出的表现。

东风吹,战鼓擂,当今世界谁怕谁。革命仍未成功,阶级斗争还在继续。
[email protected]:~> repeat 1 fortune
Matz is nice, so we are nice.
回复 支持 反对

使用道具 举报

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

本版积分规则

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

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

GMT+8, 2024-5-9 09:06

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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