热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

ruby学习小程序整理2

局部变量由小写字母或下划线(_)开头.局部变量不像全局和实变量一样在初始化前含nil值.ruby$foonilrubyfoonilrubyfooERR:(eval):1:un

局部变量由小写字母或下划线(_)开头.局部变量不像全局和实变量一样在初始化前含nil值.


ruby> $foo
nil
ruby> @foo
nil
ruby> foo
ERR: (eval):1: undefined local variable or method `foo' for main(Object)


对局部变量的第一次赋值做的很像一次声明.如果你指向一个未初始化的局部变量,Ruby解释器会认为那是一个方法的名字;正如上面所见错误信息的.

一般的,局部变量的范围会是


  • proc{...}
  • loop{...}
  • def...end
  • class...end
  • module...end
  • 整个程序(除非符合上面某个条件)

 

异常处理:rescue

在Ruby里,就像其它的现代语言,我们可以通过隔离的办法处理代码域里的异常,因此,这有着惊人的效果却又不会为程序员或以后希望读它的其它人造成过度的负担.代码域由begin开始直到遇到一个异常,这将导致转向一个由rescue标记的错误处理代码域.如果异常没发生,rescue代码就不会使用.下面的代码返回文本文件的第一行,如果有异常则返回 nil.

retry 用于 rescue 代码表示又重新执行 begin 代码.这让我们可以压缩前面的例子:


fname = "some_file"
begin
file = open(fname)
# ... process the input ...
rescue
fname = "STDIN"
retry
end

 

但这仍有一点瑕疵.一个不存在的文件将导致不停止地 retry.你在使用 retry 做异常处理时应注意到这一点.

 

如果在我们写文件的时候发生异常,文件会保留打开.我们也不希望这样的冗余出现:

 


begin
file = open("/tmp/some_file", "w")
# ... write to the file ...
file.close
rescue
file.close
fail # raise an exception
end


这是个笨办法,当程序增大时,代码将失去控制,因为我们必须处理每一个 returnbreak,.

为此,我们向"begin...rescue...end"体系中加入了一个关键字 ensure. 无论begin块是否成功,ensure代码域都将执行.


begin
file = open("/tmp/some_file", "w")
# ... write to the file ...
rescue
# ... handle the exceptions ...
ensure
file.close # ... and this always happens.
end

 

可以只用ensure或只用rescue,但当它们在同一begin...end域中时, rescue 必须放在 ensure前面.


什么是一个存取器?


我们在前面已经讨论过实变量了,但却未过多的讨论.一个对象的实变量属于它的属性,也是它与其它来自同一个类的对象的一般区别.读写它的属性是重要的;这样做需要做一个叫着属性存取器(attribute accessors)的方法.我们将很快看到我们并不是总要明确地写出存取器方法,但现在先让我们了解所有的细节.存取器的两种类型是(writer)和(reader).


ruby> class Fruit
| def set_kind(k) # a writer
| @kind = k
| end
| def get_kind # a reader
| @kind
| end
| end
nil
ruby> f1 = Fruit.new
#
ruby> f1.set_kind("peach") # use the writer
"peach"
ruby> f1.get_kind # use the reader
"peach"
ruby> f1 # inspect the object
#

 

足够简单;我们可以存取关于我们搜索的水果种类的信息.但我们的方法名还有点儿牢骚.下面的这个更简洁,也更方便.


ruby> class Fruit
| def kind=(k)
| @kind = k
| end
| def kind
| @kind
| end
| end
nil
ruby> f2 = Fruit.new
#
ruby> f2.kind = "banana"
"banana"
ruby> f2.kind
"banana"


  inspect方法


一个小插曲.你已注意到当我们试着直接观察一个对象,就会出现一些像 # 的东西.这只是个缺省的行为,我们可以自由地改变它.我们所要做的只是加一个名为 inspect 的方法.它会换一个更明了的描述对象的字符串,包括部分或全部的实变量.


ruby> class Fruit
| def inspect
| "a fruit of the " + @kind + " variety"
| end
| end
nil
ruby> f2
"a fruit of the banana variety"


一个相关的方法是to_s(转化为字符串),用在打印对象的时候.一般的,你可以认为 inspect 是一个编写或调试程序时用的工具,而 to_s 是一个美化程序输出的方法.eval.rb显示结果时总采用 inspect. 你可以用 p 方法简单的从程序里取得调试信息.


# These two lines are equivalent:
p anObject
print anObject.inspect, "/n"


 生成存取器的简单方法


因为许多实变量需要存取方法, Ruby提供了对应于标准方法的缩写.


Shortcut缩写 Effect等同于
attr_reader :v def v; @v; end
attr_writer :v def v=(value); @v=value; end
attr_accessor :v attr_reader :v; attr_writer :v
attr_accessor :v, :w attr_accessor :v; attr_accessor :w


让我们利用它加上"新鲜"信息.首先,我们自动生成了读和写方法,然后我们合并这一新信息到 inspect 中去:


ruby> class Fruit
| attr_accessor :condition
| def inspect
| "a " + @condition + @kind"
| end
| end
nil
ruby> f2.condition = "ripe"
"ripe"
ruby> f2
"a ripe banana"


更有趣的水果

 

如果没人吃我们成熟的水果,也许我们该让它们烂掉.


ruby> class Fruit
| def time_passes
| @condition = "rotting"
| end
| end
nil
ruby> f2
"a ripe banana"
ruby> f2.time_passes
"rotting"
ruby> f2
"a rotting banana"

 

但当我们这样做时,却引入了一个小问题.现在,如果我们再创造第三个水果会发生什么?记住:实变量不会在赋值前存在.


ruby> f3 = Fruit.new
ERR: failed to convert nil into String

 

inspect 方法在这里挺有理由地抱怨.我们已让它报告水果的品种和状态,但 f3 还未赋过任何值.如果我们愿意,我们可以重写inspect方法使之用 define? 方法测试实变量并只在它们存在时才报告,但也许那不是很有用;因为每一个水果都有类型和状态.看来我们应该在某种程度上确定其属性.这正是下一节我们要讨论的.

 


弹性的初始化


上面我们看到一旦一个参数被关联到一个 initialize 方法上,就无法在避免错误产生的情况下将其省掉.如果希望考虑周全,我们可以在给了参数的情况下使用它,否则使用缺省值.


ruby> class Fruit
| def initialize( k="apple" )
| @kind = k
| @condition = "ripe"
| end
| end
nil
ruby> f5 = Fruit.new "mango"
"a ripe mango"
ruby> f6 = Fruit.new
"a ripe apple"


可以在任何方法内使用缺省参数,而不仅仅是initialize.参数表(argument list)必须以有缺省值的参数结尾.


有时,提供多种初始化对象方法是有益的.虽然已超出本教程的范围,但Ruby提供了对象映象(object reflection)和可变长度的参数表(variable-length argument lists),这些都有效地促进了方法重载.

 

 

注解

本章处理一些实际问题.


语句定界符


有些语言需要一定类型的标点,一般会是分号(;)来结束程序的每一语句.Ruby却采用了shell里的shcsh的方便做法.一行中的多个语句由分号分开,但在行尾分号却并不需要;一个换行被看作一个分号.如果行以反斜杠(/)结束,随后的换行将忽略;这就允许你的单个逻辑行可以跨越数行.


注释

为什么写注释?虽然良好的代码可自成文档,但那种自以为别人能看懂并按你的方式很快去理解的想法是错误的.除此之外,你自己在离开数天后也会是另一个人;一段时间后我们忘了我们还未修补或增强程序中的哪些部分,你会说,我知道我写了这个的,但我究竟写的是些什么?

一些有经验的程序员会相当正确地指出,矛盾的和过期的注释比没有强.当然,有了注释并不意味着代码的可读性;如果你的代码不清晰,它也许是多虫的.当你学习Ruby的时候,你会发现自己需要更多的注释;然后当你可以通过更简单,优雅,可读的代码来表达思想时,它们就会减少.

Ruby遵从一些普遍的书写习惯,用井号(#)表示注释的开始.跟在#号后面直到#号这行结束为止的代码都将被解释器忽略.

同时,为了方便写大块的注释, Ruby解释器省略以"=begin""=end"开始的行中间的一切.


#!/usr/bin/env ruby

=begin
**********************************************************************
This is a comment block, something you write for the benefit of
human readers (including yourself). The interpreter ignores it.
There is no need for a '#' at the start of every line.
**********************************************************************
=end


组织你的代码

Ruby读到什么就处理什么.没有编译处理;如果有什么还没读到,就被简单地认为未定义.


# this results in an "undefined method" error:

print successor(3),"/n"

def successor(x)
x + 1
end


这并不是像一开始认为的那样,强迫你以从上至下的方式组织你的代码.只要你确保其在调用前将被定义,当解释器遇到一个方法定义时,它能安全地接受暂未定义的引用.


# Conversion of fahrenheit to celsius, broken
# down into two steps.

def f_to_c(f)
scale(f - 32.0) # This is a forward reference, but it's okay.
end

def scale(x)
x * 5.0 / 9.0
end

printf "%.1f is a comfortable temperature./n", f_to_c(72.3)


所以,一方面看起来比使用Perl或Java要稍稍不方便一些,但却没有写C那么严格(要求你永远维持所指的部分排序).将最高层的代码放在源文件的最后总是可行的.即使这样也比看见时要好的多.一个明智而无痛苦的好办法是将main定义在文件顶端,再在底端调用它.


#!/usr/bin/env ruby

def main
# Express the top level logic here...
end

# ... put support code here, organized as you see fit ...

main # ... and start execution here.


Ruby也提供了将复杂程序分割为可读,可重用,逻辑相关的大块的工具.我们已看到用 include 来访问模块.你将发现 loadrequire 也很有用.load的作用类似于文件的复制加粘贴(和C的#include处理器指令相似).require更复杂,仅在需要时才加载,而且最多加载一次.loadrequire还有其它一些区别;在语言手册,FAQ中可找到更多信息.

 

 


def first_line( filename )
begin
file = open("some_file")
info = file.gets
file.close
info # Last thing evaluated is the return value
rescue
nil # Can't read the file? then don't return a string
end
end


推荐阅读
  • 在Qt框架中,信号与槽机制是一种独特的组件间通信方式。本文探讨了这一机制相较于传统的C风格回调函数所具有的优势,并分析了其潜在的不足之处。 ... [详细]
  • STM32代码编写STM32端不需要写关于连接MQTT服务器的代码,连接的工作交给ESP8266来做,STM32只需要通过串口接收和发送数据,间接的与服务器交互。串口三配置串口一已 ... [详细]
  • 视觉Transformer综述
    本文综述了视觉Transformer在计算机视觉领域的应用,从原始Transformer出发,详细介绍了其在图像分类、目标检测和图像分割等任务中的最新进展。文章不仅涵盖了基础的Transformer架构,还深入探讨了各类增强版Transformer模型的设计思路和技术细节。 ... [详细]
  • 在尝试加载支持推送通知的iOS应用程序的Ad Hoc构建时,遇到了‘no valid aps-environment entitlement found for application’的错误提示。本文将探讨此错误的原因及多种可能的解决方案。 ... [详细]
  • 在将 Android Studio 从 3.0 升级到 3.1 版本后,遇到项目无法正常编译的问题,具体错误信息为:org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:processDemoProductDebugResources'。 ... [详细]
  • ArcBlock 发布 ABT 节点 1.0.31 版本更新
    2020年11月9日,ArcBlock 区块链基础平台发布了 ABT 节点开发平台的1.0.31版本更新,此次更新带来了多项功能增强与性能优化。 ... [详细]
  • 本文探讨了如何在 Spring MVC 框架下,通过自定义注解和拦截器机制来实现细粒度的权限管理功能。 ... [详细]
  • 在测试软件或进行系统维护时,有时会遇到电脑蓝屏的情况,即便使用了沙盒环境也无法完全避免。本文将详细介绍常见的蓝屏错误代码及其解决方案,帮助用户快速定位并解决问题。 ... [详细]
  • Zabbix自定义监控与邮件告警配置实践
    本文详细介绍了如何在Zabbix中添加自定义监控项目,配置邮件告警功能,并解决测试告警时遇到的邮件不发送问题。 ... [详细]
  • 本文探讨了如何在PHP与MySQL环境中实现高效的分页查询,包括基本的分页实现、性能优化技巧以及高级的分页策略。 ... [详细]
  • 长期从事ABAP开发工作的专业人士,在面对行业新趋势时,往往需要重新审视自己的发展方向。本文探讨了几位资深专家对ABAP未来走向的看法,以及开发者应如何调整技能以适应新的技术环境。 ... [详细]
  • 本文探讨了如何将个人经历,特别是非传统的职业路径,转化为职业生涯中的优势。通过作者的亲身经历,展示了舞蹈生涯对商业思维的影响。 ... [详细]
  • 问题描述现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能;在实际开发过程中 ... [详细]
  • 本文详细介绍了如何搭建一个高可用的MongoDB集群,包括环境准备、用户配置、目录创建、MongoDB安装、配置文件设置、集群组件部署等步骤。特别关注分片、读写分离及负载均衡的实现。 ... [详细]
  • 汇编语言:编程世界的始祖,连C语言都敬畏三分!
    当C语言还在萌芽阶段时,它首次接触到了汇编语言,并对其简洁性感到震惊。尽管汇编语言的指令极其简单,但它却是所有现代编程语言的基础,其重要性不言而喻。 ... [详细]
author-avatar
chenhe86888
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有