`

[Ruby] recursive lambda

    博客分类:
  • ruby
阅读更多
[Ruby] recursive lambda

==本文連同引文同步載於 ptt Ruby 板、LightyRoR飽和脂肪星星之一角備份區)==

很抱歉最近狀況真的是相當糟糕,導致很多事情都沒做或是沒做好。雖然以後大概也不會比較好。這樣講講就沒關係嗎?當然不是,只是替自己找一點比較能安心的藉口吧。另外本文有任何錯誤歡迎指出。

==本文開始==

我一直覺得 Ruby 缺少一個類似 self 的東西,用來表達現在這個 function/method. 這個東西有什麼用呢?其實我也不知道有什麼用,就只是單純覺得好像少了這種東西。最直覺的例子,恐怕就是具有遞迴能力的 lambda function. 我曾在 ptt Ruby 板發過一篇文,講 quine(self-reproducing programs),後來我用了 Ruby2Ruby, 寫了像這樣的結果:(飽和脂肪星有該文的備份(通常連不上))

#!/usr/bin/ruby

require 'rubygems'
require 'ruby2ruby'

(a = proc {
puts("#!/usr/bin/ruby")
puts
puts("require 'rubygems'")
puts("require 'ruby2ruby'")
puts
print("(a = ")
print(a.to_ruby)
print(").call")
}).call


最蠢的地方是明明都用 lambda(proc) 了,我卻還得把 lambda 的結果記起來留待以後使用。這樣實在是有點無趣。我希望我可以寫:

lambda{ print(this.to_ruby); print(".call") }.call

這樣不是帥氣多了嗎?於是我開始試著思索實作這東西的可能。接著我忽然想到,所謂 this 不正是指在 call stack 最上端的 function/method 嗎?因為當我們執行到這個 function/method 時,this 一定是指同一 function, 不可能忽然去指涉其他 function, 而另外一個 function 進 call stack 時,不把他解掉也不可能會執行到 this. 於是可以把 this 寫成一個 function, 不吃任何引數,回傳一個 Proc/Method 代表正在 call stack 頂端的那個 function.

而我記得 Ruby 是有方法可以去存取 call stack... 雖然好像是用很蠢的方法,也確實是有點蠢,但總之可以用模擬的。隨意 google 了一下,找到一個很簡單的方式,就是用 set_trace_func, 丟一個 callback 進去,於是 Ruby 在各個 function 間做動作的時候,都會呼叫這個 callback. 感覺就是效率會變狂差,不過呢,至少暫時是可以用的。

接著可以利用 Thread.current[:symbol] 來儲存 current thread call stack info, 任何 function call 時,push 資料進這個假的 call stack, function return 時,pop 資料出來。這樣就有一個很簡單的 call stack info 可以用了。

以下程式歡迎任意使用,licensed under Apache License 2.0, 複製到檔案用的話希望可以把以下這段 copy 到檔案最前面… XD

#    Copyright (c) 2007, Lin Jen-Shin(a.k.a. godfat 真常)
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

module Kernel
# 由於 google 到的參考程式把抓 call stack 叫 invoker,
# 所以這裡沿用他的名字。有個常數比較容易看懂程式在做什麼。
INVOKER_EVENT = 0
INVOKER_FILE = 1
INVOKER_LINE = 2
INVOKER_MSG = 3
INVOKER_BINDING = 4
INVOKER_CLASS = 5

# -1 就是 top 的意思囉
def invoker levels = -1
st = Thread.current[:callstack]
# st 有可能是 nil, 如果 invoker 先被 call 到的話。
# 雖然我不知道什麼時候會發生這種事…。levels - 2 的原因是:
# 0(stack bottom) => function that you called(this is what we want)
# 1 => Kernel#invoker
# 2 => Array #[]
# 所以去掉額外不要的額外兩個資訊。
st && st[levels - 2]
end

def this
# 因為多 call 了 this, 所以要再多去掉一個額外資訊。
info = invoker(-2)
# 這邊我本來寫成 lambda 的形式,可以正確執行,但有一個狀況
# 卻是失敗的。就是 lambda{yield}.call{} 這樣是會 error 的 :(
# 試了多次還是找不到 Proc.call 吃 block({}) 的方法,只好改寫
# 成用 Method 的形式。不知為何,Method 就可以正確使用 block...
eval("self", info[INVOKER_BINDING]).method(info[INVOKER_MSG])
end
end

set_trace_func lambda{ |*args|
case args[INVOKER_EVENT]
# 可能的有 call 和 c-call, 都是 function
when /call$/
# 這邊我搞不清楚到底是誰會先被 call, 是 call 還是 return?
# google 來的是把初始化寫在 call 裡,可是我測試都顯示是在
# return 上,所以反而是 return 的地方需要初始化。或是乾脆
# 全部拉出來在最上面初始化也可以 :)
(Thread.current[:callstack] ||= []).push args
# 同上可能有 return 和 c-return
when /return$/
(Thread.current[:callstack] ||= []).pop
end
}


以上不管有沒有問題,都可以來看一下我額外寫的幾個 unit test, 參考一下幾個我目前想到的用法。

require 'test/unit'

class TestThis < Test::Unit::TestCase
def test_fact
assert_equal(120, fact(5))
assert_equal(3628800, fact(10))
# 試用 recursive lambda
assert_equal(5040, lambda{|n| return n*this[n-1] if n>0; 1}[7])
end
def fact n
# 恐怕是最常見的用法
return n*this[n-1] if n > 0
1
end
##
def test_pass_around
# 這邊流程可能很怪,因為只是我隨便寫的,單純測試正確性罷了。
assert_equal(method(:pass_around_forward), pass_around.call(lambda{|v| v}))
end
def pass_around mode = 'pass'
case mode
when 'pass'
pass_around_forward this
else
'value'
end
end
def pass_around_forward func
assert_equal('value', func['value'])
this
end
##
def test_with_block
# 同上,流程亂寫的,單純測試正確性。
with_block{|b| assert_equal('value', b['value'])}
end
def with_block mode = 'pass', &block
case mode
when 'pass'
block[this]
else
'value'
end
end
##
def test_more_args
# Proc 就是死在這個測試,block 展開怎麼做都失敗 :(
# 改成 Method 後這邊就可以通過測試了。
more_args('get_this'){}.call('call', 1, 2, 3, 4, 5, λ{6})
more_args('get_this'){}.call('call', 1, 2, 3, 4, 5){6}
end
def more_args mode, a1=nil, a2=nil, a3=nil, *as, &block
case mode
when 'get_this'
this
else
assert_equal(1, a1)
assert_equal(2, a2)
assert_equal(3, a3)
assert_equal(4, as[0])
assert_equal(5, as[1])
assert_equal(nil, as[2])
assert_equal(6, yield)
assert_equal(6, block.call)
end
end
end

最後,我可以把 quine 改寫成:

#!/usr/bin/ruby

require 'rubygems'
require 'ruby2ruby'
require 'invoker'

def f
puts("#!/usr/bin/ruby")
puts
puts("require 'rubygems'")
puts("require 'ruby2ruby'")
puts("require 'invoker'")
puts
print(this.to_ruby)
print("\nf")
end
f


為什麼要用 def f; end 呢??這樣不就失去 this 的意義了??因為不曉得為什麼,Ruby2Ruby 跑 lambda + this 都會有奇怪的 runtime error, 而這個錯誤是來自 class RubyToRuby 的 rewrite_defn(exp), 最後的:「raise "Unknown :defn format: #{name.inspect} #{args.inspect} #{body.inspect}"」這我一時不知道要怎麼解決,不知道會是誰的錯,所以只好暫時用 def f; end 這種蠢方式了。

另外,還有一些無聊的花枝可以玩:

def just_print_it n
print n
this
end

然後就可以:

just_print_it(1)[2][3][4][5]

輸出:12345

def add_attr attr
($attr ||= []).push attr
this
end

add_attr('hello')['world']['and then?']['good-bye']

或是很簡單的流程控制:

def fight step = :ready
case step
when :ready
ready this
when :go
go this
when :finish
finish this
end
end

def ready callback
play_ready_animate
if blah
callback[:go]
else
callback[:finish]
end
cleanup_ready
end

def go callback
gogogo
if blah
callback[:finish]
else
callback[:ready]
end
cleanup_go
end


類推,我也不知道有什麼用,搞不好有什麼 cleanup 是要等所有的事都做完才能做?總而言之,就是這樣啦,沒什麼營養,但我卻覺得很重要的功能。在很多時候可以少打很多字。搞不好哪一天也會想到什麼真的很有價值的應用也說不定。總覺得真的有太多事是無心插柳柳橙汁。雖然大部份都來不及喝就乾掉了。

2007.04.16 godfat 真常
分享到:
评论

相关推荐

    Recursive_recursive_

    标题"Recursive_recursive_"和描述"Introduction to recursive programming"都指向了递归编程的主题,这是计算机科学中的一个核心概念。下面我们将深入探讨递归编程的基础、原理以及如何在实际编程中应用。 递归的...

    recursive sql.txt

    postgre sql recursive sql. 在postgoresql 中使用recursive的脚本实现循环查询结果

    Recursive Mathematics - Volume 1 Recursive Model Theory part2

    In this book, hyperarithmetic theory is developed at length and used to lift classical recursion theory from integers to recursive ordinals (metarecursion). Two further liftings are then made, first ...

    前端开源库-mkdir-recursive

    "mkdir-recursive" 是一个专门针对这种情况的开源库,它提供了在前端环境中实现递归创建或删除目录的功能,无论是异步还是同步方式。这个库对于那些需要在用户浏览器上进行本地存储或者模拟服务器端文件操作的应用...

    Laravel开发-laravel-recursive-collection

    `laravel-recursive-collection`是一个专为解决此类问题设计的包,它扩展了Laravel的内置集合(Collections)类,允许开发者以更优雅的方式对嵌套数组和关联数组进行迭代和转换,生成嵌套的集合对象。 一、Laravel...

    Node.js-recursive-watch最小递归文件监视器

    这就是`recursive-watch`库的作用,它提供了一个最小化的递归文件监视器,可以监测指定目录及其子目录下的文件变化。 **一、安装与使用** 在你的项目中,你可以通过npm(Node.js的包管理器)来安装`recursive-...

    recursive.rar

    本资源"recursive.rar"包含了关于如何将递归函数转换为非递归函数的方法,以及使用Python语言编写的示例代码,旨在帮助开发者理解这一转换过程,并对比不同实现方式的执行效率。 递归函数的主要特点在于它会将大...

    Theory.of.Recursive.Functions

    Theory.of.Recursive.Functions

    Introduction to Recursive Programming azw3

    Introduction to Recursive Programming 英文azw3 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者或csdn删除

    Introduction to Recursive Programming epub

    Introduction to Recursive Programming 英文epub 本资源转载自网络,如有侵权,请联系上传者或csdn删除 本资源转载自网络,如有侵权,请联系上传者或csdn删除

    Recursive Methods in Economic Dynamics.Stokey.Lucas

    Recursive Methods in Economic Dynamics.Stokey.Lucas

    Recursive Mathematics - Volume 1 Recursive Model Theory part3

    Recursive Mathematics - Volume 1 Recursive Model TheoryRecursive Mathematics - Volume 1 Recursive Model TheoryRecursive Mathematics - Volume 1 Recursive Model Theory

    Recursive-Gaussian-filter.rar_recursive gaussian

    本压缩包文件"Recursive-Gaussian-filter.rar_recursive gaussian"包含了基于OpenCV库实现的递归高斯滤波器代码,文件名为"opencv.cpp"。 首先,我们要理解高斯滤波器的工作原理。高斯滤波器是基于二维高斯函数的...

    Recursive Projection Clustering

    Recursive Projection Clustering(RPC)是一种在机器学习领域中用于数据聚类的算法,它结合了投影和递归的思想,旨在发现数据集中的潜在结构。RPC 方法以其独特的方式处理高维数据,试图找到能够最好地区分不同类别...

    Super-Recursive Algorithms_C++_algorithms_

    《Super-Recursive Algorithms》这本书深入探讨了C++编程语言中的超级递归算法,这是一类在计算理论和算法设计中至关重要的技术。超级递归不仅仅是简单的递归调用,它涉及到了更复杂的数学和计算机科学概念,如元...

    introduction-recursive-programming

    Recursion is one of the most fundamental concepts in computer science and a key programming technique that, similarly to iteration, allows computations to be carried out repeatedly

    Recursive LS - Recursive Search-开源

    "Recursive LS - Recursive Search" 是一个开源项目,其主要功能是提供一个跨平台的递归文件搜索库。这个库的设计目标是为了在各种操作系统上高效地查找指定目录及其子目录下的文件,而无需依赖特定的系统API。项目...

    Recursive Mathematics - Volume 1 Recursive Model Theory

    In this book, hyperarithmetic theory is developed at length and used to lift classical recursion theory from integers to recursive ordinals (metarecursion). Two further liftings are then made, first ...

    A recursive procedure for calculation of some compound distributions

    ### 递归程序在复合分布计算中的应用 #### 引言与背景 本文探讨了复合分布的计算问题,尤其关注计数分布具有特定性质的情况,即连续概率比可表示为两个多项式的比值。该文由Ole Hesselager撰写,发表于1994年的...

Global site tag (gtag.js) - Google Analytics