Programming Ruby(读书笔记)-9章(表达式)

Ruby中任何语句都有一个相应的返回值。

a = b = c = 0                  # => 0
[ 3, 1, 7, 0 ].sort.reverse  # => [7, 3, 1, 0]

 if与case块返回的是最后一个语句的执行结果。

song_type = if song.mp3_type == MP3::Jazz
                      if song.written < Date.new(1935, 1, 1)
                        Song::TradJazz
                      else
                        Song::Jazz
                      end
                   else
                     Song::Other
                   end
#case的返回结果
rating = case votes_cast
             when 0...10 then Rating::SkipThisOne
             when 10...50 then Rating::CouldDoBetter
             else Rating::Rave
             end

 

9.1 操作表达式

+ - * / 是操作符,它们实际上也是方法。a*b+c:在a对象上调用方法*,参数是b,返回的结果对象上调用方法+,参数是c。

a, b, c = 1, 2, 3
a * b + c # => 5
#a.*是合法的
(a.*(b)).+(c) # => 5

 ----实例方法可以被重新定义------

#下面重定义Fixnum类的方法+方法
class Fixnum
  alias old_plus + #引用旧的+
  def +(other)
    old_plus(other).succ
  end
end
1 + 2 # => 4
a = 3
a += 4 # => 8
a + a + a # => 26

这种有用的做法是在自定义的类里添加支持操作符的方法

class ScoreKeeper
  def initialize
    @total_score = @count = 0
  end
  def <<(score)
    @total_score += score
    @count += 1
    self              #返回自己
  end
  def average
    fail "No scores" if @count.zero?   #如果count是0则报错
    Float(@total_score) / @count
  end
end
scores = ScoreKeeper.new
scores << 10 << 20 << 40
puts "Average = #{scores.average}"
produces:
Average = 23.333333333333332

 --------some_obj[1, 2, 3]表示调用some_obj的[]方法,参数是1 2 3---

class SomeClass
  def [](p1, p2, p3)
  ...
  end
end

 -----------[]=方法--------------

#参数:前n个表示下标,后面的是对应的值
class SomeClass
  def []=(*params)
    value = params.pop #把最后的值先取出来
    puts "Indexed with #{params.join(', ')}" #剩下的都是下标
    puts "value = #{value.inspect}"
  end
end

 

9.2 各式各样的表达式

上面表达式都是显示的操作表达式,下面会讲一些不是那么显然的表达式,比如if 与case

 

Command Expansion扩展命令

 当使用``或者%x{}包含时,ruby执行它们做为操作系统命令。对应的返回是该命令的标准输出,可能会包含换行。

`date`                 # => "Mon May 27 12:30:56 CDT 2013\n"
`ls`.split[34]         # => "newfile"
%x{echo "hello there"} # => "hello there\n"
for i in 0..3
  status = `dbmanager status id=#{i}`
  # ...
end

$?是一个全局变量,用来获取命令的执行结果状态 

 

重新定义反引号

alias old_backquote `
def `(cmd)
  result = old_backquote(cmd)
  if $? != 0   #命令的执行状态可通过$?获取
    puts "*** Command #{cmd} failed: status = #{$?.exitstatus}"
  end
  result
end
print `ls -l /etc/passwd`
print `ls -l /etc/wibble`
produces:
-rw-r--r-- 1 root wheel 5086 Jul 20 2011 /etc/passwd
ls: /etc/wibble: No such file or directory
*** Command ls -l /etc/wibble failed: status = 1

 

 Assignment赋值

lvalue = rvalue  =>将右边引用的值赋给左边引用,并且返回rvalue做为表达式的结果。

a = b = 1 + 2 + 3
a # => 6
b # => 6
a = (b = 1 + 2) + 3
a # => 6
b # => 3
File.open(name = gets.chomp)

 

#注意,这个返回的结果是2
class Test
  def val=(val)
    @val = val
    return 99
  end
end

t = Test.new
result = (t.val= 2)
puts result
prduces:
2

Parallel Assignment(并行赋值)

在Ruby中可以同时将多个值赋给多个变量

a,b = 1,2
a,b = b,a #=>[2,1]右边先计算出为2,1,然后再把它们赋值给左

 并行赋值的过程:Ruby发现右边有多个元素,会先把它们计算出来,然后放置到一个数组中(如果已经是数组则不做),然后查看右则,如果右则只有一个元素(没有分割符分开),则将整个数组分配给它,否则,逐个赋值,超出的部分被抛弃。不够则无值。

a = 1, 2, 3, 4      #=>  a=[1, 2, 3, 4] 
b = [1, 2, 3, 4]    #=>  b=[1, 2, 3, 4] 
a, b = 1, 2, 3, 4   #=>  a=1, b=2
c, = 1, 2, 3, 4     #=>  c=1 

 

Splats(使用*作前缀) and Assignment

#先将右边的元素解开逐个计算,然后,再设给左边
a, b, c, d, e = *(1..2), 3, *[4, 5]  #=> a=1, b=2, c=3, d=4, e=5 

 如果右边的有spat,则会赋给它一个数组

a, *b = 1, 2, 3  #=> a=1, b=[2, 3]
a, *b = 1        #=> a=1, b=[] 

 如果splat不是在最后一个位置呢?会先把单个的匹配会,把剩下再分给spat,如果没有了,就分给个空数组。

*a, b = 1, 2, 3, 4          #=> a=[1, 2, 3], b=4 
c, *d, e = 1, 2, 3, 4       #=> c=1, d=[2, 3], e=4 
f, *g, h, i, j = 1, 2, 3, 4  #=> f=1, g=[], h=2, i=3, j=4 

 与方法的入参一样,可以通过这种方式,忽略掉多余的参数

first, *, last = 1,2,3,4,5,6  #=># first=1, last=6

 

Nested Assignment(嵌套赋值)

左边的变量可使用()包括表示是嵌套赋值,则提取右边对应位置的值赋给它们,如果左边是数组,右边就一个值,则只会给左边嵌套的第一个赋值。

 

a, (b, c), d = 1,2,3,4       #=> a=1, b=2, c=nil, d=3 
a, (b, c), d = [1,2,3,4]     #=> a=1, b=2, c=nil, d=3 
a, (b, c), d = 1,[2,3],4     #=> a=1, b=2, c=3, d=4 
a, (b, c), d = 1,[2,3,4],5   #=> a=1, b=2, c=3, d=5 
a, (b,*c), d = 1,[2,3,4],5   #=> a=1, b=2, c=[3, 4], d=5 

Other Forms of Assignment

#通过定义+方法,来实现a +=1,这种操作的支持
class Bowdlerize
  def initialize(string)
    @value = string.gsub(/[aeiou]/, '*')
  end
  def +(other)
    Bowdlerize.new(self.to_s + other.to_s)
  end
  def to_s
    @value
  end
end
a = Bowdlerize.new("damn ") # => d*mn
a += "shame"              # => d*mn sh*m*

#这边这个操作就是a = a.+ ("shame")
#a + 这种操作方法是合法的,但是a to_s这种不合法,需写成a.to_s

 

9.4 Conditional Execution(条件执行)

Boolean Expressions

在Ruby中,除非值为nil或者显示的设值为false(constant false),否则都为true。"cat", 99, 0, and :a_song are all considered true。

 

And, Or, and Not

注意:这些操作符返回的并不只是true或false。而可能是实际的值。

 

&& and :如果第一个为false,则返回第一个,否则返回第二个的值.

nil && 99    # => nil
false && 99 # => false
"cat" && 99 # => 99

|| or:如果第一个为true,则返回第一个,否则返回第二个的值

----优先级---

and 与or的优先级相同,但是&&的比||高。

 

常常使用 ||=来给变量设默认值,即如果变量没有被赋值给赋给它指定的值。

var ||="default value"
#var = var || "default value

! not:是取返回。! (1 and 2)

 

defined?

如果参数没有定义则返回nil,如果已经定义,则返回参数的描述。

如果参数是yield,并且有代码块在当前的上下文中,则返回串"yield"

defined? 1          # => "expression"
defined? dummy      # => nil
defined? printf    # => "method"
defined? String    # => "constant"
defined? $_        # => "global-variable"
defined? Math::PI  # => "constant"
defined? a = 1     # => "assignment"
defined? 42.abs    # => "method"
defined? nil       # => "nil"

 

Comparing Objects(比较操作)

作为boolean操作的补充,Ruby支持 ==,===,<=>,=~,eql?,equal?

除了<=>以外(使用Object.instance_methods发现也有这个方法啊?),在基类Object中都已经有,但是常会被覆盖来表示对应的语义,比如Array的==,会覆盖以表示两个数组的长度一致,并且每个元素相等。

 

==与=~的相返操作是!=与!~。再调用!=或!~时,Ruby会先操作这两个方法名,如果没有找到,会调用==与=~,然后把结果取返。

 

操作符的语义:

==   是否值相等

=== 右边是否在左边中

<=>  产生-1,0,+1,小于,等于,大于

=~    正则表达式的匹配

eql?  接收者与参数有相同的类型与相等的值。1 == 1.0 返回true,但是1.eql?(1.0)返回false

equal?  接收者与参数有相同的对象ID

 

-----区间用于boolean表达式---

exp1..exp2 :false...->exp1(=true)->true...->exp2(true)->false

 

if and unless Expressions(如果与如果不)

if artist == "Gillespie" then
  handle = "Dizzy"
elsif artist == "Parker" then
  handle = "Bird"
else
  handle = "unknown"
end
#如果语句块有多行,则可省去then。如果只有一行,则不能省略
if artist == "Gillespie" then handle = "Dizzy"
elsif artist == "Parker" then handle = "Bird"
else handle = "unknown"
end

 如果不

unless duration > 180
  listen_intently
end
#如果duration 不大于 180,则执行

 

if and unless Modifiers(if unless的改良版)

mon, day, year = $1, $2, $3 if date =~ /(\d\d)-(\d\d)-(\d\d)/
puts "a = #{a}" if $DEBUG
print total unless total.zero?

#读取文件内容,跳过注释,不解析空行
/^\s*$/表示空行
/^$/表示没有空格的空行
File.foreach("/etc/passwd") do |line|
  next if line =~ /^#/                 # Skip comments
  parse(line) unless line =~ /^$/ # Don't parse empty lines
end

 

9.5 case Expressions(case)

 

一种用法类似于if..elsif语句
case
when song.name="Misty"
  puts "Not again!"
when song.duration > 120
  puts "Too long!"
when Time.now.hour > 21
  puts "it's too late!"
else
  song.play
end
第二种用法,case 添加被评估语句。
case command
when "debug"
  dump_debug_info
  dump_symbols
when /p\s+(\w+)/
  dump_variable($1)
when "quit", "exit"
  exit
else
  print "Illegal command: #{command}"
end
#同样,可以写when ... then ...,这种单行样式。
 case target

 

 when keyword

这种语句的实际原理是依赖===,Ruby拿target与每个when的语句进行比对。只要类实现了===方法,就可被使用在这种场景。

 

case line
when /title=(.*)/
  puts "Title is #$1"
when /track=(.*)/
  puts "Track is #$1"
end
 Ruby的class是Class类的一个实例,这个类定义了===方法,用于判断参数是否为接收者的实例或子类型实例。

 

 

case shape
when Square, Rectangle
# ...
when Circle
# ...
when Triangle
# ...
else
# ...
end
 

 

9.6 Loops

 

while condition
  #...
end
util则是相反,它执行块内的语句,直到条件满足以后才不执行
util play_list.duration > 60
  play_list.add(song_list.pop)
end
 
a = 1
a *=2 while a < 100
a  #=>128
a -= 10 until a< 100
a #=>98
 Ruby的区间(Range)可被用来作为触发器(开关),当某事发生时为true,然后,再在某事发生时再次变为false。
a = (11..20).collect { |i| (i%4==0)..(i%3) ? i : nil}
a ->[nil, 12,nil,nil,nil,16,17,18,nil,20]

a = (11..20).collect { |i| (i%4==0)...(i%3) ? i:nil}
a ->[nil,12,13,14,15,16,17,18,nil,20]
#..会在一次调用中求解exp2,而...则不会,它是在后面的调用中再求解。
 
#下面的示例演示从行的开始为third开始打印,直到开始为fifth为止。
file = File.open("ordinal")
while line = file.gets
  puts(line) if line =~ /third/ .. line =~/fifth/
end
produces:
third
fourth
fifth
#区间与boolean表达式结合
File.foreach("ordinal") do |line|
  if (($. == 1) || line =~ /eig/) .. (($. == 3) || line =~ /nin/)
    print line
  end
end
produces:
first
second
third
eighth
ninth
在这儿插入一个Ruby的特殊变量
$! 最近一次的错误信息
$@ 错误产生的位置
$_ gets最近读的字符串
$. 解释器最近读的行数(line number)
$& 最近一次与正则表达式匹配的字符串
$~ 作为子表达式组的最近一次匹配
$n 最近匹配的第n个子表达式(和$~[n]一样)
$= 是否区别大小写的标志
$/ 输入记录分隔符
$\ 输出记录分隔符
$0 Ruby脚本的文件名
$* 命令行参数
$$ 解释器进程ID
$? 最近一次执行的子进程退出状态
$:  default search path (array of paths)
 
如果while 与util的改良样式用于begin...end块时,不管条件是否为true,都至少会执行一次,如下:
print "Hello\n" while false
begin
  print "Goodbye\n"
end while false
produces:
Goodbye
 

Iterators

#使用times
3.times do
  print "ho"
end
#使用upto
0.upto(9) do |x|
  print x, " "
end
produces:
0 1 2 3 4 5 6 7 8 9
#使用step,步数为3
0.step(12, 3) {|x| print x, " " }
produces:
0 3 6 9 12
#数组对象的each
[ 1, 1, 2, 3, 5 ].each {|val| print val, " " }
produces:
1 1 2 3 

 类可以通过提供each方法,来提供被客户端迭代。

File.open("ordinal").grep(/d$/) do |line|
  puts line
end
produces:
second
third

 loop循环

loop do
  # block...
end

 

 

for...in

可以通过迭代+块代码来替换。

#for...in的语法
for i in ['fee', 'fi', 'fo', 'fum']
  print i, " "
end
for i in 1..3
  print i, " "
end
for i in File.open("ordinal").find_all {|line| line =~ /d$/}
  print i.chomp, " "
end
produces:
fee fi fo fum 1 2 3 second third

 

 只要类中定义了each(大小写敏感)方法,我们就可以使用for...in来迭代这个对象

class Periods
  def each
    yield "Classical"
    yield "Jazz"
    yield "Rock"
  end
end
periods = Periods.new
for genre in periods
print genre, " "
end
produces:
Classical Jazz Rock

 

break,redo and next

break:跳出

redo:从开始再来一次,但是注意它不会再执行条件语句,或者在iterator时,不获取next元素

next:在iterator时,跳过到loop的结尾,即本次不执行下面的代码。

 

#next break与redo的示例
while line = gets
  next if line =~ /^\s*#/ # skip comments
  break if line =~ /^END/ # stop at end

  # substitute stuff in backticks and try again
  redo if line.gsub!(/`(.*?)`/) { eval($1) }

  # process line ...
end

 这三个关键字还可以用于代码块

i=0
loop do
  i += 1
  next if i < 3
  print i
  break if i > 4
end
produces:
345

 

9.7 Variable Scope,Loops,and Blocks(循环或块内的变量范围)

 

#下面的演示块内可以访问块外,而如果有同名,使用自己块内的变量
x = "initial value"
y = "another value"
[1, 2, 3].each do |x|
  y = x + 1
end
[x, y] #=>["initial value", 4]

 

#这个示例演示块外代码不会被执行到时,还是可以被使用
a = "never used" if false  #a不会赋值
[99].each do |i|
  a = i
end
a #=>99
Ruby的拦截器只需要能看到左边的代码就可以了。

 也可以在块代码的参数部分,显示的定义内部使用的变量名,这样即使重名,也不会与外部的冲突。

#在参娄的后面,使用分号与之隔开
square = "yes"
total = 0
[ 1, 2, 3 ].each do |val; square|
  square = val * val
  total += square
end
puts "Total = #{total}, square = #{square}"
produces:
Total = 14, square = yes

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值