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

Project1

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

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

  [复制链接]

Lv1.梦旅人

梦石
0
星屑
61
在线时间
24 小时
注册时间
2008-8-5
帖子
1924
42
发表于 2010-10-16 06:33:14 | 只看该作者
本帖最后由 紫苏 于 2010-10-16 06:38 编辑

通过上一讲,相信同学们都对 Ruby 1.9 的字符串编码有了一定的了解。在底层,1.9 的编码功能实际上是通过一个叫 M17N 的开源引擎实现的,专门用来处理多语言字符串,它在 C 的层面把字符串包装成了一个拥有一些属性的对象。
用心的同学们肯定会记得,我们曾经提到了 Encoding 对象这个概念。Encoding 对象,自然是表示一个编码对象,我们可以用它来代替用来表示编码名称的字符串。Encoding 这个类没有提供实例化的方法,但是它在自己的命名空间中预定义了大量的 Encoding 对象作为静态成员,各自表示一种 Ruby 1.9 支持的编码方式。这是一种经典的设计模式——多例(multiton)模式,与之类似的有单例(singleton)模式,即类只有一个单一实例的模式。

  1. p Encoding.name_list
复制代码
这句脚本会打印出当前支持的所有编码。类似于 name_list 这样的静态方法(类对象单例方法),还有:
Encoding.aliases,它返回一个以别名为键,基础编码名称为值的 Hash 对象,告诉了你所有编码的所有别名,如 CP936 就是 GBK 的别名;
Encoding.compatible?,它测试两个字符串的编码是否兼容,兼容编码的字符串可以直接串连起来;
Encoding.default_external、Encoding.default_external=,它们分别获取、设置默认的外部编码,用于处理输入、输出流时的字符串编码;
Encoding.default_internal、Encoding、default_internal=,它们分别获取、设置默认的内部编码,用于内部数据读写的转码;
Encoding.find,通过编码名称字符串获取对应的 Encoding 对象;
Encoding.list,罗列出所有加载了的 Encoding 对象(之前提到的 name_list 是编码名称的字符串);
Encoding.locale_charmap,返回当前 locale 的代码页,这是由操作系统的设置而决定的,比如 PRC locale 的代码页就是 GBK;

Encoding 的实例也可以通过预定义的常量来引用,如:Encoding::UTF_8、Encoding::GBK。
Encoding 实例的方法,有 dummy?、name 和 names 这三个。dummy? 是用来测试接收者 Encoding 对象是否是 dummy 的(很明显!),即 M17N 并没有真正地实现、支持这种编码,但 Ruby 仍然把它加入了进来,如:UTF-7;name 显然是返回编码的名称;names 则是返回接收者编码对象的所有有效名称:

  1. p Encoding::UTF_8.names
复制代码
输出:
["UTF-8", "CP65001"]
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
61
在线时间
24 小时
注册时间
2008-8-5
帖子
1924
41
发表于 2010-10-15 08:16:19 | 只看该作者
本帖最后由 紫苏 于 2010-10-16 00:59 编辑

Ruby 1.9 的一大改进就是对编码的支持有了质的飞跃。今天我们先来看一下 1.8 中令人头疼的字符串编码、解码相关的问题是如何在 1.9 中轻易解决的。
内置库中,String 类多了几个方法——encode、encode!、encoding,前两个都是把接收者字符串按照另一个指定字符集编码的方法,分别是非破坏性和破坏性版本;第三个是用来获取编码方式的访问器。

  1. #encoding: UTF-8
  2. p 'hello'.encode('UTF-16LE')
复制代码
这里顺便介绍一下第一行的作用,它是 Ruby 1.9 的新功能。什么,这不就是行注释?是的,你没看错,它确实是一行注释,但却被称为“magic comment”,因为它是 Ruby 1.9 推荐的用来指定源文件编码的方式。如果一个 Ruby 源文件第一行包含注释,且注释中包含 `coding: ' 这个字串(coding + 冒号 + 空格),那么在 `coding: ' 之后的内容就会被用来作为当前源文件的编码名称,如这里的 UTF-8。在类 Unix 操作系统上,源文件可以包含 Shebang,而 magic comment 则必须是紧接着 Shebang 的下一行,不能有空行。
所以,我们这里是让源文件按照 UTF-8 格式来编码,而这会使得字符串字面值的编码也成为 UTF-8。之后我们通过 String#encode 方法把一个简单的字符串转换为了小端序 UTF-16 编码,也就是平时我们引用 `Unicode' 时所指的编码,这是我们在 Windows 操作系统上调用 API 处理多字节字符(如:中文字符)相关的操作时需要的编码方式。输出:

"h\x00e\x00l\x00l\x00o\x00"

我们就是这么简单地把字符串编码为了本地 API 可识别的形式,而在 Ruby 1.8 中,还得通过调用本地 API 来进行转码,如 Windows 平台上的 MultiByteToWideChar 函数。
如果我们没有给 String#encode 传递参数,那么它会默认使用 Encoding.default_internal(这个以后再讲)来编码。如果传递了两个字符串参数,那么后一个字符串参数则会被作为源编码方式:
  1. p '你好'.encode('UTF-16BE', 'GBK')
复制代码
这里就是把 '你好' 这个字符串当做 GBK 字符串编码为大端序 UTF-16。
String#encode 还接受第三个参数,这个参数指定了编码时额外的选项,包括源字符串中的非法字符以及目标编码中不存在对应代码点的字符的默认替代字符的选项(比如 `?',我们经常能在某些非 Unicode 程序中看见代码页不匹配时出现的一大串 `????',就是因为这些程序用来解码的字符集中没有对应的源字符串中的代码点,这时程序就选择用 `?' 来代替这个它不知道如何表示的字符),不同平台之间回车、换行符转换的选项(Windows 是 `\r\n', Linux 是 '\n', Mac OS 是 '\r')以及 XML PCDATA 特殊字符转换的选项(如 < 转换为 &lt; 等),具体可以参考官方文档中的详细表格,这里给一个处理中文时的情况:

  1. #encoding: GBK
  2. p '你好'.encode('ISO-8859-1')
复制代码
以上会抛出一个 Encoding::UndefinedConversionError 异常,因为 ISO-8859-1 没有中文字符的代码点,而我们又没有指定替换字符。这样的话:

  1. #encoding: GBK
  2. p '你好'.encode('ISO-8859-1', undef: :replace, replace: '?')
复制代码
就可以了,输出:"??"。要注意这里后面实际上是一个参数,是一个以符号为键的 Hash 对象(见本贴之前的相关内容)。

String#encode! 方法和 String#encode 用法一模一样,只不过是原地(破坏性)算法。String#encoding 可以用来获取接收者字符串当前的编码方式。

String#encode 和 String#encode! 表式编码的参数不一定是编码名称字符串对象,也可以是一个 Encoding 对象。关于 Encoding 对象,我们下次再讲解。

点评

……= =b 这让我写Marshal转换的时候十分愉悦  发表于 2010-10-15 17:59
回复 支持 反对

使用道具 举报

Lv2.观梦者 (管理员)

八云紫的式神

梦石
0
星屑
564
在线时间
1243 小时
注册时间
2008-1-1
帖子
4282

烫烫烫

40
发表于 2010-10-13 16:58:41 | 只看该作者
看紫苏大人讲了好多1.9特性,于是我也来弄一下。。。不过级别完全不够所以只说个自己用过的小细节

在1.8中,取string里的一个字符会得到一个整数,其值是那个字符的ASCII码,除非你是C语言控,否则你不会认为这是一项功能而是一个bug
指的庆幸的是在1.9这个缺陷得到了改变,取出的将是一个只包含那个字符的string对象

a = "ABC"
p a[0]
#=> 65 in 1.8
#=> "A" in 1.9

ruby1.9乃太萌了,紫苏大人给点资料吧- -最好有中文

点评

不知道 Programming Ruby 1.9 有没有被翻译成中文 = =  发表于 2010-10-13 19:56
rm for linux(wine)制作中,期待夏娜SAMA能实现到webrm上
回复 支持 反对

使用道具 举报

Lv3.寻梦者

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

贵宾

39
 楼主| 发表于 2010-10-11 12:45:16 | 只看该作者


Kernel、Object、Module、Class间的关系。

简洁、明了、没有胃溃疡。

点评

其实匿名类都是不会被 #superclass 获取到的,不只是 Kernel 的 mixin 而已  发表于 2010-10-11 13:58

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

使用道具 举报

Lv3.寻梦者

宛若

梦石
0
星屑
1563
在线时间
526 小时
注册时间
2007-8-19
帖子
1493

极短24参与开拓者

38
发表于 2010-10-10 11:12:05 | 只看该作者
为什么此帖被顶上来了- -
然后不留些东西貌似过意不去……
RM的Interpreter是个很诡异的存在,尤其是数据结构保存形式- -
显示文章等带换行的数组保存形式是
事件开始代码
事件开始代码+400(貌似脚本是300)
同时……在eval脚本的时候有一处诡异的地方:
  1.     # 评价
  2.     result = eval(script)
  3.     # 返回值为 false 的情况下
  4.     if result == false
  5.       # 结束
  6.       return false
  7.     end
复制代码
这样导致了如果事件页脚本返回的是false的话就会导致index不能推进,事件卡住的问题- -
于是当脚本是赋值变量为false时只能占用那少得可怜的脚本框写下return true之类的结束语……而且这种错误通常无法发现- -
最后……感谢II提醒脚本卡住的原因- -

点评

本来想创造一楼清一色紫苏的,你个坏人  发表于 2010-10-11 05:29
看了半天才发现是倒序的- -||| 我自行爆破吧 - -|||  发表于 2010-10-10 16:35
= =一天一枚诶……  发表于 2010-10-10 15:15
[url=http://rpg.blue/thread-219730-1-1.html]http://unhero.sinaapp.com/wi.php[/url]
[color=Red]如你所见这是个死坑,没错这就是打我的脸用的[/color]
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
61
在线时间
24 小时
注册时间
2008-8-5
帖子
1924
37
发表于 2010-10-10 10:09:59 | 只看该作者
本帖最后由 紫苏 于 2010-10-13 19:54 编辑

Ruby 1.9 引入了纤程(Fiber)的概念。
提起纤程,我们自然而然地就想到了线程(Thread),而事实上当你对比了“纤”和“线”这两个字后,你大概就能猜到,纤程实际上是一种轻量级的线程了。轻量级在这里是指纤程的体积比线程小,更容易维护、调试,但也不具备一些线程才能提供的复杂功能。
在并行计算领域,我们使用线程来构建一个抢先式多任务模型;而纤程则是用于合作式多任务模型。
抢先式多任务模型,顾名思义,是指执行任务的对象的工作模式是抢先式的,工作调度完全由一个具有权利的、客观的存在进行。比如:三个兄弟抢着用电脑,谁也不让谁,但是当妈妈在家的时候,就能够合理地分配、调度、切换各个兄弟使用电脑的时间。又如:我们使用的操作系统,其进程和线程的调度就是一种抢先式任务模型。
合作式多任务模型,显然就是指执行任务的对象之间有团队精神,可以协同工作。第一个对象完成了一部分工作,就可以把成果交给第二个对象,让他在成果的基础上继续工作,并等待他完成。所以,我们使用纤程的主要目的,就是实现这种所谓的“协程”。比如:动物园的售票系统模型,观光者需要售票员处理售票相关的事项,然后把票递给自己;而售票员需要观光者把钱递给自己。这是在纤程的概念还没有出现时就存在于线程技术中的生产者与消费者关系的概念。
来看一个简单的例子:

  1. fibonacci = Fiber.new {
  2.         a = b = 1
  3.         loop {
  4.                 b += a
  5.                 a = b - a
  6.                 Fiber.yield b
  7.         }
  8. }

  9. 10.times {
  10.         p fibonacci.resume + 1000
  11. }
复制代码
输出:
1002
1003
1005
1008
1013
1021
1034
1055
1089
1144

我们这里是实现了打印出一个 Fibonacci 序列中前 10 个数 各加上 1000 后的结果的协程。fibonacci 是我们的生产者纤程,它的任务是生产出 Fibonacci 序列。在循环内部,Fiber.yield 会暂停执行,把执行权让给调用纤程的代码,并把参数作为 Fiber#resume 的返回值传递回去。在纤程外部,我们有一个十步的循环,作用是把 Fibonacci 序列前 10 个数各加上 1000 并打印出来。每次调用 Fiber#resume,都会恢复纤程的执行,并返回 Fiber.yield 的参数。这时如果我们给 resume 传递了参数,该参数就会被作为 Fiber.yield 的返回值传递给纤程(当然我们这里没有用到这个功能,但在模拟更复杂的合作式多任务时,是极有可能需要双向交互的)。
同样的效果,我们可以用一个循环就完成,即在生产 Fibonacci 数的同时打印(循环内部生成一个就打印一个),但这被认为是差劲的设计模式,因为它把生产和消费这两个过程混合到了一起。使用纤程的话,我们就能使它们泾渭分明。
同样的效果,我们也可以用线程来实现。但线程相比于纤程是重量级的,我们需要考虑对象锁之类的条件变量来进行线程同步,同时还得考虑潜在的线程安全问题。而我们的纤程,则拥有同步的天性,使得我们在使用时无须顾虑各种线程相关的复杂问题。
Ruby 1.9 内置的纤程只适用于小规模协程(二人合作),因为 Fiber.yield 只能把执行权返回给 Fiber#resume 的调用者。如果想实现规模更大的协程,我们就需要使用 Ruby 1.9 标准库中的 Fiber 了(不属于这帖讨论范畴)。

点评

萌物  发表于 2010-10-13 17:20
moy
每天都有新知识,欧也~  发表于 2010-10-11 15:55
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
61
在线时间
24 小时
注册时间
2008-8-5
帖子
1924
36
发表于 2010-10-9 08:35:46 | 只看该作者
Ruby 1.9 有了新的 Hash 结构和语法。在 1.9 中,你可以用以下方式更快速地写出一个包含以符号为键的元素的 Hash 对象:
  1. #coding: GBK
  2. p ({ 赵天吉: "大刀会", 朱红灯: "义和团" })
复制代码
输出:
{:赵天吉=>"大刀会", :朱红灯=>"义和团"}
注意这种新的 Hash 字面值只针对于符号键。
结构方面,也有了一个大革命。Hash,顾名思义,一堆杂乱的符号,自然没什么顺序可言,随着我们对 Hash 的了解越深,“Hash 不保证顺序”的概念就越来越深入我们脑海。然而这次,顽皮的 Ruby 又一次颠覆了用户的观念:Hash 也可以是有序的!
在 1.8 中,Hash 对象即是使用分离链接法解决哈希碰撞的散列表。表中每一项需要四个东西:散列码、键的引用、值的引用、下一个散列码相同的元素的引用。为了实现有序的散列表,Ruby 1.9 在表的元素结构体中增加了两个新的指针:指向按顺序排列的前一个和后一个元素的指针,这使得在对散列表进行插入和删除操作时能够维护一个有序的链,实际上就是一个和旧的散列表结构穿插在一起的双向循环链表结构,这两种结构的并集就是新的 Hash 结构了。这个改动的代价是:1.9 的 Hash 的 插入和删除变得更慢了些。这个改动的好处是:Hash 的遍历、迭代、枚举也有序了。同时,因为内部维护着双向循环链表,1.9 遍历 Hash 的速度也比 1.8 快,因为 1.8 必须要遍历整个散列表的桶,自然也就必须经过一些桶中的空位。
对于为了有序遍历而损失插入、删除的效率是否值得的问题,仁者见仁,智者见智。有些人用散列表来存储一种字典式的结构,然后需要把内容按照有序的方式打印出来,这是常有的;而有些人仅仅是用散列表来进行快速的保存和读取数据。不过我个人倒是有点不赞成直接改动原来的 Hash 类,我们完全可以设计一个新的类,比如 OrderedHash,这样就能让用户有一个选择。

点评

散列表和数组在不同的方面效率不同,还是给用户保留选择为好  发表于 2010-10-13 19:50
我极为赞成像php那样把hash并入Array  发表于 2010-10-13 17:21
嘛,高中的时候自学了点html和js的皮毛…然后后来重拾了下,看了点js的面向对象,然后就被Ruby带走了  发表于 2010-10-11 13:43
有序哈希我一般的针对方法是同步的双数组= =b  发表于 2010-10-10 15:16
67以前是搞JS的?o.o  发表于 2010-10-10 00:01
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
61
在线时间
24 小时
注册时间
2008-8-5
帖子
1924
35
发表于 2010-10-8 07:01:16 | 只看该作者
本帖最后由 紫苏 于 2010-10-8 07:13 编辑

Ruby 1.9 引入了新的语法支持块局部变量,同时也改变了原来的块参数行为。在块原本的参数列表中,使用一个分号 `;' 可以分隔块的参数和局部变量声明,如下:
  1. l1 = l2 = 1
  2. lambda{ |arg1, arg2 ; l1, l2|
  3.         l1 = 1234567
  4.         l2 = 7654321
  5.         p "参数是#{arg1},#{arg2}"
  6. }.call(126, 127)
  7. p l1                                        # => 1
  8. p l2                                        # => 1
复制代码
其中 arg1、arg2 是块的参数(call 的时候需要传递),而 l1、l2 则是块局部变量的声明。块局部变量的作用域仅限于块的上下文,且声明的块局部变量必须出现在块参数后面。
这种机制也是为了进一步完善闭包,毕竟早在 Lisp 时代就有 let 语法支持闭包局部变量。这实际上就是变量遮蔽机制在闭包这个场合的一个特例——作用域小的变量遮蔽了作用域大的变量,使得作用域大的变量不可见。
块的参数在 Ruby 1.8 中并不一定是局部的,如果外部有同名变量,那么它引用的仍然是外部的同名变量。在 Ruby 1.8 中,以下代码中的块变参数赋值会改变块外部变量的值(也就是没有了遮蔽):
  1. l1 = l2 = 1
  2. lambda{ |l1, l2|
  3.         p "参数是#{l1},#{l2}"
  4.         l1 = 1234567
  5.         l2 = 7654321
  6. }.call(126, 127)
  7. p l1                                        # => 1234567
  8. p l2                                        # => 7654321
复制代码
而在 Ruby 1.9 中,块的参数永远是局部的。
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
61
在线时间
24 小时
注册时间
2008-8-5
帖子
1924
34
发表于 2010-10-7 07:55:13 | 只看该作者
Ruby 1.9 支持带缺省值的参数出现在不带缺省值的参数之前,而 Ruby 1.8 及其它很多支持参数缺省值的语言都要求带缺省值的参数只能出现在列表最后。

  1. def foo(a = 0, b)
  2.         p a+b
  3. end

  4. p foo(2)       # => 2
  5. p foo(1, 2)  # => 3
复制代码
当然,带缺省值的参数必须挨在一起,如下情况是不合语法的:
  1. def bar(a, b = nil, c, d = nil, e)
  2. p [ a, b, c, d, e ]
  3. end
复制代码
这可能是由于 Ruby 解释器使用的 LR(1) 解析器不能轻易处理这种情况。
带缺省值参数和可变长度参数列表可以混用,但前者必须出现在后者之前:
  1. def foo(a = 0, *args)
  2.     p [a, args]
  3. end

  4. foo(5, 6, 7, 8) # => [5, [6, 7, 8]]
  5. foo()           # => [0, []]
复制代码

点评

GOOOOD,太萌了太萌了  发表于 2010-10-13 17:03
不一样吧……  发表于 2010-10-7 20:03
紫苏大人重复了哟~愚者在16楼写过~  发表于 2010-10-7 14:49
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
61
在线时间
24 小时
注册时间
2008-8-5
帖子
1924
33
发表于 2010-10-6 07:04:23 | 只看该作者
Ruby 1.9 的块的参数也可以使用 & 来把默认的块转换为 Proc 对象了:
  1. l = lambda { |&l| l.call }
  2. l.call { p 'nested' }
复制代码
而这在 1.8 中则是不合其语法的。相同的效果也可以由我们之前提到的 `->' 运算符实现:
  1. l = ->(&l) { l.() }
  2. l.() { p 'nested' }
复制代码
回复 支持 反对

使用道具 举报

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

本版积分规则

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

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

GMT+8, 2024-6-9 15:30

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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