2017 年 2 月 27 日
最近又需要嵌入CRuby,又遇到了这个问题,
主要是当使用net/http等socket相关的库时,会提示找不到read_nonblock方法。
这个问题的解决方案很简单,补充一个定义即可
| class IO def read_nonblock(size, buf = nil, exception) __read_nonblock(size, buf, exception) end end |
但是使用ruby或者irb时就没有这个问题,为什么呢
(这也不是操作系统相关的问题,虽然我用的是windows)
irb下通过IO.instance_method(:read_nonblock).source_location可以发现这个方法的定义在一个叫”<internal:prelude>”的文件中定义,显然是内部自己的奇怪操作。
这个定义可以通过ruby_init_options函数来初始化,然而。
然而经过搜索发现这个函数一直有人提出,将它导出作为feature,结果从1.9到2.4都没有实行,这个函数直到现在,现成编译的版本从未导出符号。
所以,现在初始化ruby解释器,如果不想从源码修改去掉函数的static存储类关键字这里开始,一个比较合适的写法是:
| int argc = 0; char **argv = 0; static char* args[] = {(char *)"ruby", #ifdef WIN32 (char *)"nul" #else (char *)"/dev/null" #endif }; ruby_sysinit(&argc, &argv); ruby_init(); ruby_init_loadpath(); ruby_process_options(2, &args); //假装有个ruby程序执行了一个程序,从而隐含调用prelude定义 |
2016 年 8 月 10 日
最近做codewars上的Haskell的水题的时候(难题我也不会),基本上是尽量用一种point-free的风格(https://en.wikipedia.org/wiki/Tacit_programming)去写的。我个人理解也就是函数的实现不写出参数,只通过curry和组合等来实现,例如:
| -- let add a b = a + b let add = (+) -- let factorial n = product [1..n] let factorial = product . enumFromTo 1 -- let isPalindrome str = reverse str == str let isPalindrome = reverse >>= (==) -- 下面几个的point-free style作为练习 -- let k f x = f -- let g x y f = f x y -- let triple f x = f x x x |
另外这里也不认可出现用lambda形式代入参数如:
| let g = \x y f -> f x y --这也不是本文讨论的 |
这里用一个生成的方法来实现一个比较通用的方案,其实应该也可以用haskell的point-free风格代码来实现这套代码而不是生成,这里用Ruby生成。
注意到:
也可以写成
| let f x y = ((a.) . b) x y -- 即 let f = (a.) . b |
每次多一个参数就会是最里面多一个
| ... (a .) . ... ... ( ((a .) .) . (a .)) . ... |
等等的结构,也就是组合前面是一个(a .)的形式
另外,如果我们把tuple视为栈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | > let f = (3, (2, 1)) > let push = (,) push 4 f -- (4, (3, (2, 1))) > let pop = snd pop f -- (2, 1) > let modify = first modify (+1) f -- (4, (2, 1)) > let apply = (uncurry ($) . (first *** id)) > let g = ((+1), (2, 3)) apply g -- (3, 3) 另外实现几个从forth拿过来的栈运算 >let vdup = modify (fst >>= (,)) 复制栈顶 (a, b) -> b vdup f -- (3, (3, (2, 1)) >let vswap = modify ((fst . snd) &&& (fst &&& (snd . snd))) 交换栈顶二个元素 (a, (b, c)) -> (b, (a, c)) vswap f -- (2, (3, 1)) >let vover = modify ((fst . snd) &&& id) over运算, 把栈顶的下一个元素复制一份压在顶上,(a, (b, c)) -> (b, (a, (b, c))) vover f -- (2, (3, (2, 1))) 等等可以把forth的栈操作都实现出来 |
于是这样就可以实现一组DSL(https://github.com/Artoria/pf-builder)了,比如海伦公式的实现
heron a b c = let s = (a + b + c) / 2 in s * (s – a) * (s – b) * (s – c)
下面的思路是通过build生成函数体
首先把三个参数a b c转成[c, b, a]
然后应用 (/2) . sum得到s
然后把s变成(s-), map到[c, b, a]上,变成[s-c, s-b, s-a], 然后对他它用product, 并且乘上这个积和s
就是结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | puts Builder.new{ push_arg modify "(: [])" push_arg push_const "(:)" apply apply push_arg push_const "(:)" apply apply modify_dup "(/2) . sum" vover vover modify "(-)" push_const "map" apply apply modify "product" push_const "(*)" apply apply modify "sqrt" }.finish! |
得到的结果,反正就是不能看了:
| ((((((((((((((((((((((((((( fst . (first (sqrt))) . (uncurry ($) . (first *** id))) . (uncurry ($) . (first *** id))) . ((,) ((*)))) . (first (product))) . (uncurry ($) . (first *** id))) . (uncurry ($) . (first *** id))) . ((,) (map))) . (first ((-)))) . ((fst . snd) &&& id)) . ((fst . snd) &&& id)) . (first ((/2) . sum))) . (fst >>= (,))) . (uncurry ($) . (first *** id))) . (uncurry ($) . (first *** id))) . ((,) ((:)))) .) . flip (,)) . (uncurry ($) . (first *** id))) . (uncurry ($) . (first *** id))) . ((,) ((:)))) .) . flip (,)) . (first ((: [])))) .) . flip (,)) undefined) |
另外,因为point-free的函数也是函数,可以嵌套
比如fib n = foldr (\_ (x, y) -> (y, x+y)) (1, 0) [1..n]
| puts Builder.new { push_arg modify "enumFromTo 1" push_const "(1, 0)" push_const Builder.new{ push_arg vpop push_arg modify "snd &&& (liftA2 (+) fst snd)" }.finish! push_const "foldr" apply apply apply modify "snd" }.finish! |
得到的结果
| ((((((((((( fst . (first (snd))) . (uncurry ($) . (first *** id))) . (uncurry ($) . (first *** id))) . (uncurry ($) . (first *** id))) . ((,) (foldr))) . ((,) (((((((( fst . (first (snd &&& (liftA2 (+) fst snd)))) .) . flip (,)) . snd) .) . flip (,)) undefined)))) . ((,) ((1, 0)))) . (first (enumFromTo 1))) .) . flip (,)) undefined) |
2016 年 5 月 7 日
毕竟Bellard大牛的OTCC里面就是到处飞机器码,并且,他是一边解析词法符号一边就直接产生了,真是棒棒的不按套路却又无比正经的写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | =begin Author: 晴兰@66RPG UID=131313 LICENSE: CC-BY-SA 3.0 [url]http://creativecommons.org/licenses/by-sa/3.0/deed.zh[/url] Purpose: Simple Machine Code Generator. Limited operation supported =end CallWindowProc = Win32API.new 'user32', 'CallWindowProcW', 'pLLLL', 'L' def prolog(num_of_var = nil) [0x55, 0x8b, 0xec].pack("C*") + (num_of_var ? [0x83, 0354, num_of_var * 4].pack("C*") : "") end def epilog [0xc9, 0xc2, 0x10].pack("CCS") end def render(list, num_of_var = nil) prolog(num_of_var) + render_rec(list) + epilog end def check_var(list) return -4 * list[1] if list[0] == :var raise "lvalue required for #{list.inspect}" end def render_rec(list) case list.first when :lit [0xb8, list[1]].pack("CL") when :arg [0x8b, 0105, 4 + list[1] * 4].pack("C*") when :do list[1..-1].map{|x| render_rec(x)}.join when :var [0x8b, 0105, -4 * list[1] ].pack("C*") when :assign id = check_var(list[1]) render_rec(list[2]) + [0x89, 0105, id].pack("C*") when :+, :-, :&, :^, :| render_rec(list[2]) + "\x50" + render_rec(list[1]) + [0132, "+|ab&-^".index(list.first.to_s) << 3 | 3, 0302].pack("C*") when :<, :>, :'==', :'!=', :>=, :<= v = list.first.to_s l = v.length f = v.ord rescue v[0] u = "0000G+000000<HF>".index((f + (l > 1 ? 10 : 0)).chr) render_rec(list[2]) + "\x50" + render_rec(list[1]) + [0132, 073, 0302, 0x0f, 0x90 + u, 0300, 0x81, 0340, 0xff, 0x00, 0x00, 0x00].pack("C*") when :nil; "" when :if cond, thenpart, elsepart = list[1..-1].map{|x| render_rec(x)} thenpart += [0xe9, elsepart.length].pack("CL") cond += [0x85, 0300, 0x75, 0x05, 0xe9, thenpart.length].pack("CCCCCL") cond + thenpart + elsepart when :for init, cond, incr, body = list[1..-1].map{|x| render_rec(x)} addbyte = [0x85, 0300, 0x75, 0x05, 0xe9, body.size + incr.size + 5].pack("CCCCCL") fbody = body + incr + [0xe9, -(body.size + incr.size + cond.size + addbyte.size + 5)].pack("CL") init + cond + addbyte + fbody end end x = render( [:do, [:assign, [:var, 1], [:lit, 0]], [:for, [:assign, [:var, 2], [:lit, 1]], [:<, [:var, 2], [:lit, 101]], [:assign, [:var, 2], [:+, [:var, 2], [:lit, 1]]], [:assign, [:var, 1], [:+, [:var, 1], [:var, 2]]] ], [:var, 1] ], 2 ) =begin int var1 = 0; for(int var2 = 1; var2 < 101; var2+=1) var1 += var2; return var1 =end p CallWindowProc.call x, 0, 0, 0, 0 #=>5050 |
其实很多东西跟普通ruby使用gem和bundle是一样的,但是RM没有自带rubygems,因此有两种思路,一种是给RM嵌入一个rubygems,一种是利用另一个Ruby(也就是实际运行bundle的ruby.exe等)来发现require需要的文件和依赖关系。
下面以第二种思路为例,例子任务是使用iniparse这个gem来帮助RM里面读Game.ini。
注意:RPGMaker系列本身不支持载入.so文件,因此能用到的gems只能是纯Ruby形式的源文件组成的代码,这个文章与这个限制无关,也无意绕过这个限制。只是提供了另一种管理脚本插件的思路:借用rubygems。产生的gem目录也可以最后放在加密的rgssad(rgss2a/rgss3a)文件里面并用某种手段当做本地目录读出来,从而保证了加密保护游戏程序的语义,当然这里也不讨论对gem目录的加密和解包具体方法。
1. 首先像正常使用bundle一样在Game.exe目录下建立一个gemfile:
| source 'http://ruby.taobao.org' gem 'iniparse' |
2. 用bundle在工程目录安装所需的gems:
用cmd在工程目录下运行下列代码。
| bundle install --path gems |
3. 在RM的脚本编辑器中插入一段辅助代码,让require可以自动去找到我们指定的gem:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | def rb(code, args = nil) open "run.rb", "w" do |f| f.write " class Output def <<(obj) puts [Marshal.dump(obj)].pack('M') end end $output = Output.new " f.write code end system "ruby run.rb #{args}> list.txt" Marshal.load File.read('list.txt').unpack("M").first end module Kernel alias oreq require $:.concat rb %{ $output << $: } def require(a) u = rb %{$output << Gem.find_files(ARGV[0])}, a u.select(&FileTest.method(:file?)).any?{|req|oreq req} end Object::RUBY_VERSION = rb %{$output << RUBY_VERSION} require 'rubygems' alias require oreq end |
4. 这个时候在脚本编辑器之后可以插入使用的代码了,这里我们用的是RGSS3(RPG Maker VX Ace)的代码例子:
| require 'iniparse' msgbox IniParse.parse(File.read('Game.ini'))["Game"]["Scripts"] |
5. 最后,运行的方式是在cmd下运行下列代码,你也可以建立快捷方式:
| bundle exec "Game.exe console" |
2014 年 2 月 23 日
0.02
Class:
#基于类的通用cache系统,比如
x = Bitmap.cache(“Graphics/Parallaxes/Mountain4”) #你懂的
y = Bitmap.cache(“Graphics/Parallaxes/Mountain4”)
# x.object_id == y.object_id应该成立,同一个位图
#Bitmap.clear_cache #你懂的
#也可以是自定义对象
class A
def initialize(a, b)
@a, @b = a, b
end
end
x = A.cache(3, 5)
y = A.cache(3, 5)
#应该是同一个对象
# RGSS3Facets::CachePolicy 奇怪的东西:
Bitmap.cache_policy = RGSS3Facets::CachePolicy::Default #Cache在内存中,以Hash的形式记录,类似于默认脚本的Cache或者RPG::Cache
Bitmap.cache_policy = RGSS3Facets::CachePolicy::Marshal.new(Bitmap) #Cache不是在内存中而是以Marshal形式存到磁盘上
Bitmap.cache_policy = RGSS3Facets::CachePolicy::BareMarshal.new(Bitmap) #Cache不是在内存中而是以Marshal形式存到磁盘上,而且没有Marshal头,仅仅是直接调用了_dump和_load
Bitmap:
#Bitmap::MarshalPolicy bitmap的marshal方案可以是
Bitmap::MarshalPolicy = Bitmap::BitmapMarshalPolicy::Raw # [width, height, data].pack(“LLa*”)
Bitmap::MarshalPolicy = Bitmap::BitmapMarshalPolicy::RawCompress # [width, height, Zlib::Deflate.deflate(data)].pack(“LLa*”)
Bitmap::MarshalPolicy = Bitmap::BitmapMarshalPolicy::BitmapFile # Marshal内容相当于savebmp做的,也就是一个合法的bmp文件内容
Object:
load_script(name) #也就是eval read_data(name), TOPLEVEL_BINDING, “Load Script: #{name}”, 1
0.01
Object:
font.assign(:size=>5, :color=>Color.new(255,0,0,255)) # font.size=5; font.color = Color.new(255,0,0,255)
object.self # 返回本身,为了某些场合可以用&:self
object.deepcopy #=>某种深拷贝,也就是Marshal.load Marshal.dump object
object.template(“{{a}}, {{b}}”)#=>模板替换,也就相当于是”#{obj.a},#{obj.b}”,但模板内容可以是变量。
私有:
read_data(“Data/1.txt”) #=>读取Data/1.txt的内容,可以是加密包中的内容
write_data(“Data/1.txt”, “Hello”)#=>把Hello写到Data/1.txt,只能是磁盘上的,不会变更加密包
sugar(/\b类\b/){“class”} #=>宏替换,$RGSS_SCRIPTS.each{|x| x[3].gsub!(/\b类\b/){“class”}}
with(obj){}#=>obj.instance_eval{}
a = 5
ref{}[“a”]=3
a = ref{}[“a”]#=> 上下文变量的字符引用
Module:
Sprite_Base.each_object{|x| p x}#遍历Sprite_Base的实例
Vocab.each_constant{|value, name| p value, name}#遍历常量,以值和名的形式
MyModule.to_class #生成一个include了本模块的类(可以new了)
attr_constant :a, 5# 相当于def a(); 5; end
Proc:
lambda{|x| a}.call(a,b,c)可以写成lambda{|x| a}.update(a,b,c) lambda{|x| a}.fire(a,b,c)随便什么方法名都行
用method_missing实现的,不一定是call了
Range:
(1..5).clamp(a) # 如果a<1返回1 如果a>5返回5 否则返回a
Rect:
rect.each_slice(5, 5) do |r| … end # 将这个rect均匀分成5×5的部分,每一部分调用一下迭代器
Exception:
ex.raise # reraise ex
ex.translate(新的消息) # 返回一个和ex只有消息不同的异常对象,也就是ex.exception的别名
IO:
IO.binread(“1.txt”) #二进制读取1.txt
IO.binwrite(“1.txt”, “Hello”)#二进制写入1.txt
Symbol:
to_proc(这个功能VA本来就有)
String:
self.note.match_notes “<itemid = {{item}}>”, “<skillid = {{skill}}>”#如果self.note是一个”<itemid = 5> <skillid = 3>”的话,返回一个hash {“item”=>5, “skill”=>3}
Bitmap:
bitmap.repeat_blt(bitmap.rect, another_bitmap, another_bitmap.rect)#尽可能平铺绘制another_bitmap的rect部分的内容
bitmap.address:位图地址
bitmap.savebmp(“1.bmp”): 以bmp格式保存为1.bmp
bitmap.savepng(“1.png”):以png格式保存为1.png
bitmap.getBitmap(rect) : 返回rect部分构成的新位图
bitmap.split(:A=>[0,0,128,128], :B=>[0,128,128,128]):返回一个hash,其中hash[:A]= bitmap.getBitmap(Rect.new(0,0,128,128)),类推
bitmap.with_tone!(tone)
bitmap.with_tone(tone) #用指定的色调tone来处理本位图
(下面是还在测试和修正的内容)
Sprite:
sprite.image #返回sprite能看到的图形,没有考虑tone,wave系列,考虑了src_rect
Plane:
plane.image #返回plane能看到的图形,没有考虑tone,考虑了屏幕大小和循环的情况