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

Project1

 找回密码
 注册会员
搜索
楼主: harinlen

[版务] 【活动】【VA】RGSS 3 入门教程大家写(更新13/02/15)

  [复制链接]

Lv1.梦旅人

梦石
0
星屑
129
在线时间
124 小时
注册时间
2011-9-12
帖子
76
发表于 2013-12-28 16:47:08 | 显示全部楼层
本帖最后由 mxymxy 于 2013-12-28 16:54 编辑

过程抽象(上)

这里兑现之前的承诺,把过程抽象简单讲了,大神勿喷……



一、过程抽象

“程序设计一个很重要的地方就是控制复杂度,而过程是一种黑盒封装的形式,某种程度上……”
“说人话!”
好好好,简要地说,就是说它是程序变得简单了。这个谁都知道的。我们定义过程之后,就可以在使用时不用关心它的实现,而仅仅关注于它的结果Ruby的过程抽象也是一样,但是它和一般的过程其实是有区别的,因为原则上任何能进行相同计算的东西都可以用同一个过程处理。假设我们写了一个用乘法来计算数字乘方得函数,而字符串如果也能计算乘法,那么我们就可以直接用这个函数来计算字符串的乘方,而不需要重新定义其他过程。
我们通过使用过程(不论是存在的还是假想的),就将问题一步步地分解成子问题,不断简化它们,直到问题得以解决。比如说,我们要计算一个函数的平方根,于是我们就是求y^2=xy。我们先有一个猜测值,我们评价这个猜测值,不够好的话我们就不断改进这个猜测。于是我们大致能写出下面的程序:
  1. def square-root(x)
  2.   guess=1.0
  3.   score=evaluate(...)
  4.   until good-enuf?( ...)
  5.     guess=improve(...)
  6.   end
  7.   return guess
  8. end
复制代码
然后我们再分别实现evaluate(可以是计算x^2guess的差值,也可以是计算improveguess的改进程度),good-enuf?(是精确到绝对小数位或者是相对位数),和improve(可以用二分法或者是牛顿法),我们就写出了这个程序。当然我们也许不会吧这三个过程单独提出来,而是会写到一起去,但是我们至少在心里清楚,它们是独立的过程块,满足如下依存关系:
square-root
         ↓
evaluate,good-enuf?,improve
         ↓
......

再比如说,我们要计算一个线段围成的复杂几何图形的面积,我们可以先把它分成多边形,然后计算这些多边形的面积;计算多边形的面积时我们又需要用到向量的叉积;……
程序,不论多大多小,其结构都可以像这样被断成一个个较小的、易于控制的块,这些块又进一步分段成更小的块;而且,当我们要重复利用某一个块的时候,我们不需要重写代码;当某一个块出错的时候,我们也只需要修改一处即可。这就是过程抽象的直接价值。

二、λ对象和它的环境

假设我们要对数组里的所有元素求和,我们可能会写出这样的函数:
  1. def arr_sum(ary)
  2. i = 0
  3. sum=0
  4. while i < ary.length
  5.   sum+=ary[i]
  6. end
  7. return sum
  8. end
复制代码
现在我们又需要计算平方和,立方和……我们需要写很多类似的代码,他们的差别仅仅在于对sum的计算,于是我们考虑,应该写出一个通用函数,用我们来告知其我们要计算的是平方还是立方。于是有了这样的代码:
  1. # 注意这段代码是执行不了的,因为sqr被定义为object的私有方法的缘故
  2. def sqr(x)
  3.   return x*x
  4. end

  5. def arr_do(ary, p)
  6.   i=0
  7.   sum=0
  8.   while i<ary.length
  9.     sum+=p.call(ary[i])
  10.     i+=1
  11.   end
  12.   return sum
  13. end

  14. p arr_do([1,2,3],:sqr.to_proc) #思路可行,语法无效
复制代码
如果仅仅是为了临时调用,名字也可以省了,就成了这样:
  1. def arr_do(ary, p)
  2.   i=0
  3.   sum=0
  4.   while i<ary.length
  5.     sum+=p.(ary[i])
  6.     i+=1
  7.   end
  8.   return sum
  9. end

  10. p arr_do([1,2,3],->x{x*x}) # =>14
复制代码
这样,arr_do就是一个接受过程参数的一般性的方法。
有时候,我们需要计算数组中每个元素除数字y的值,y运行时刻确定。于是:
  1. def y_over_x(y)
  2.   return ->x{y/x}
  3. end

  4. def arr_do(ary, p)
  5.   i=0
  6.   sum=0
  7.   while i<ary.length
  8.     sum+=p.(ary[i])
  9.     i+=1
  10.   end
  11.   return sum
  12. end

  13. p arr_do([1,2,3],y_over_x(6)) #=>11
  14. x_d_54= y_over_x(54)
  15. p arr_do([1,2,3], x_d_54) #=>99
复制代码
于是我们发现,一个过程可以:
1.      用变量命名
2.      提供给过程作为参数
3.      成为返回值
4.      包括在数据结构里
因此过程是一个对象,和数字、字符什么的没有任何区别的对象。事实上它不过是一段二进制存储的代码而已,和数据存储于同一区域。那么我们就得到了方法的对象,也就是所谓的里的λ对象。我们将看到,通过这一对象,我们将走入Ruby编程的第三范式——(不完全的)函数式模型。
最好用的定义(也有其他写法,但不方便):
->(参数列表;局部变量列表){语句列表}
圆括号是可选的,但是最好加上;{}之前必须紧凑书写,多余的空格不要加,不然即使符合松本先生的语法定义,在RMVA里也会报错
例子:
  1. fac = ->(x,y,factor=2;ret){
  2.   ret = [x * factor, y * factor]
  3.   return ret
  4. }

  5. p fac.(1, 2) #=>[2,4]
  6. #注意fac.是fac.call的简写
  7. p fac.(1, 2, 3) #=>[3,6]
复制代码
如果你想把一个lambda对象传递给需要代码块的函数,请用&修饰它,不然就会被当做普通参数传递而报错:
  1. p ([1,3,2,6,5].sort &->(a,b){b-a})
  2. #=>[6,5,3,2,1]
复制代码
然后,我们还缺少什么呢?外部变量。比如说y_over_x,它的返回值理所当然地需要能调用y,不然整个架构就会变得毫无意义,这就是所谓的“闭包”。可是,注意到我们在调用它返回的λ时,y_over_x的生命周期已经结束了,我们如何才能正确取得y的值呢?
松本先生告诉我们:“闭包并不持有它需要变量的值,而是确实动态地持有变量本身。闭包延长了局部变量的生命周期。”(这和C#C++都是不完全一样的,请务必不要混淆
也就是下面的例子:
  1. access_pair = ->(intval){
  2.   value = intval
  3.   getter = ->(){ value }
  4.   setter = ->(newval){ value = newval }
  5.   return getter, setter
  6. }

  7. gX, sX = access_pair.(10)
  8. gY, sY = access_pair.(5)
  9. p gX.() #=>10
  10. p gY.() #=>5
  11. sY.(8)
  12. p gY.() #=>8
  13. p gX.() #=>10
复制代码
看到了吗?gXsX将共享同一个access_pair中的value,而gYsY将共享另一个access_pair中的value,这是λ闭包包括了创建者的(变量)环境。
而当我们不希望λ干扰创建者的(变量)环境时,我们就要这么写:
  1. ->(;value){value}
复制代码
这里value是真正的λ的局部变量了,它只属于λ的环境,不属于创建者的环境。
于是,我们将形如->(V){E}式子中的V包含的变量(不论是参数还是局部变量),称为是“被绑定到λ上的”,不包含在V中的变量称为是“自由出现的”。脱离了->(V)的约束的{E},里面的V也同样叫做是自由出现的。自由出现的变量一般是定义它的地方的参数或局部变量。于是被绑定的变量和自由出现的变量共同构成了λ的(变量)环境。

二、λ对象的性质

Ruby中,我们不能判断两个λ是不是相等的,只能判断他们中的一个是不是另一个dup出来的。否则,即使完全一样,也不是相等的:
  1. ->(x){x}==->(x){x} #=>false
复制代码
所以在Ruby中比较λ是不适当的行为。
但是这并不妨碍我们在逻辑上认为这两个λ是等价的。以下我们使用“=”来表示式子两边在逻辑上是等价的,尽管编程中无法区别。
为了研究λ的性质,我们再引入这样一个假想的函数repl,我们要求repl(expr,V,W)的结果是把式子expr中所有V的自由出现变换成W的自由出现,即:
  1. repl(x,x,y)=y
  2. repl(return [x,z,->(x){x}];,[x,z],[y,w])=return[y,w,->(x){x}];
复制代码
然后我们可以得出以下三条规则:

1.alpha变换:->(V){E}=->(W){repl(E,V,W)}
这是很好理解的,名字不过是个标记,对参数和局部变量统一换名当然不会对代码块的工作产生任何影响。通过这一规则,我们认为->(x){x}->(y){y}就是等价函数。

2.beta消解或beta规约:令F=->(V){E},则F.(W)=repl(E,V,W)
这条规则描述了λ是如何起到“函数”的作用的,就是通过实际参量替换形式参量,然后代入计算求值。同时这和alpha变换也是一脉相承的,因为不管你用什么形式上的名字,一规约全换掉了,也就没有差别。这一规则在逻辑推导中常常被用来化简表达式。

3.eta等价:对两个表达式EE’,如果对所有的X(->(V){E}).(X)=(->(V){E’}).(X),那么E=E’,反之亦然
这条规则表明,所有输入相同参数得到相同返回值的函数(λ)是逻辑上相同的函数(λ)。

好了剩下的且看下节分解。

点评

SICP啥的呢~  发表于 2014-6-30 10:46
学过C++以后一回头看lambda总算是理解了= =比wiki说的要更加简单呢……  发表于 2014-6-22 15:27
@DeathKing 其实还有《programming languages and lambda calculi》这本神书,略难,目前正在攻读……  发表于 2014-1-18 23:10
哈哈。前面是受了SICP的启发吧?  发表于 2014-1-18 14:48
前面懂了,后面这部分好可怕0 0  发表于 2013-12-29 13:37

评分

参与人数 2星屑 +18 梦石 +1 收起 理由
taroxd + 1 精品文章
DeathKing + 18 括号神教,一统江湖,千秋万代~.

查看全部评分

本帳號已經作廢。新ID是「湖中神劍」。
回复 支持 反对

使用道具 举报

Lv3.寻梦者 (版主)

…あたしは天使なんかじゃないわ

梦石
0
星屑
2207
在线时间
4033 小时
注册时间
2010-10-4
帖子
10779

开拓者贵宾

发表于 2013-12-29 09:41:20 | 显示全部楼层
本帖最后由 taroxd 于 2013-12-29 09:48 编辑
mxymxy 发表于 2013-12-28 16:47
过程抽象(上)

这里兑现之前的承诺,把过程抽象简单讲了,大神勿喷……


看得有点难的赶脚……

点评

一下就把λ演算就给引入过来,当然咯。  发表于 2014-1-18 14:50
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
730
在线时间
9 小时
注册时间
2014-2-13
帖子
2
发表于 2014-2-15 20:57:03 | 显示全部楼层
satgo1546 发表于 2012-10-1 14:23
接下来应该讲注释了吧,我顺便讲掉吧(?

注释
注释就是防止自己写的脚本时间长了忘记脚本这一段是什么 ...

问一下,如何将把注释当脚本使用

点评

写注释的时候不用# 用【text = "注释内容"】 之后【eval(text)】执行 前后两个text一致就可以了  发表于 2014-2-15 21:10
這是不可能的吧......  发表于 2014-2-15 21:08
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
730
在线时间
9 小时
注册时间
2014-2-13
帖子
2
发表于 2014-2-15 23:03:55 | 显示全部楼层
好吧,我重新问一下,就是在脚本中进行定义,然后在事件中的注释打上类似调用方法的代码进行文本的调用。
如图一,实际上的效果是演奏音效、然后待机、然后输出了一段带语音的文本和相应的图(实际效果类似于图二),请问如何实现
Z]_EF]7BUK4)7VEG)HNJ~44.jpg

这是图二

这是图二

点评

这和脚本的注释无关了。然后你目前的效果用公共事件即可。  发表于 2014-2-16 13:37
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
50
在线时间
64 小时
注册时间
2010-12-31
帖子
66
发表于 2015-4-26 13:26:40 | 显示全部楼层
satgo1546 发表于 2012-10-1 14:23
接下来应该讲注释了吧,我顺便讲掉吧(?

注释
注释就是防止自己写的脚本时间长了忘记脚本这一段是什么 ...

msgbox "a的值是#{a}"  不是把#后面的注释掉了吗? 为什么会显示a的值是1 呢?

点评

↓ 他语法没错啊,在不会引起歧义的情况下括号是可以省略的。另外#{}这个是 内嵌表达式 在 表达式是为【# $ 或 @ 记号的变量名称】时是可以省略{}的  发表于 2015-4-27 10:56
这坟挖的……首先你语法就错了,应该是msgbox("a的值为#{a}")这样,然后#这个符号在RGSS的字符串中是一个特殊的转义符,但特例仅此一个,只需记住即可   发表于 2015-4-26 13:35
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
50
在线时间
64 小时
注册时间
2010-12-31
帖子
66
发表于 2015-4-26 22:07:27 | 显示全部楼层
mxymxy 发表于 2013-12-28 16:47
过程抽象(上)

这里兑现之前的承诺,把过程抽象简单讲了,大神勿喷……

VoCab : : SaveMessage 问问大神这个句子是什么意思?

点评

我现在帮你把楼下的帖子删除。以后请不要这样连帖哦。看得很乱,而且你直接回复我们是收不到提醒的。  发表于 2015-4-27 13:44
获取VoCab模块下的SaveMessage常量  发表于 2015-4-27 11:00
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
133
在线时间
24 小时
注册时间
2018-1-31
帖子
4
发表于 2019-11-16 00:25:48 | 显示全部楼层
頂一個  來此學習腳本
回复 支持 反对

使用道具 举报

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

本版积分规则

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

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

GMT+8, 2024-4-18 14:31

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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