Programming Ruby(読書ノート)-9章(式)

15826 ワード

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
" `ls`.split[34] # => "newfile" %x{echo "hello there"} # => "hello there
" 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は右側に複数の要素があることを発見し、まずそれらを計算し、1つの配列に配置し(すでに配列であればしない)、右側を見て、右側に1つの要素(分割子が分かれていない)がある場合は、数のグループ全体を割り当てます.そうしないと、1つの値を割り当て、超えた部分は捨てられます.足りなければ価値がない.
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(ネスト割り当て)
左側の変数には、()がネストされた値であることを示す場合、右側の対応する位置の値を抽出して割り当て、左側が配列である場合、右側が1つの値である場合、左側にネストされた最初の値のみが割り当てられます.
 
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:1番目がfalseの場合、1番目を返します.そうでない場合、2番目の値を返します.
nil && 99    # => nil
false && 99 # => false
"cat" && 99 # => 99

|or:1番目がtrueの場合、1番目を返します.そうでない場合、2番目の値を返します.
----優先順位---
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の==など、対応する意味を表すために上書きされることが多く、2つの配列の長さが一致し、各要素が等しいことを表すために上書きされる.
 
==と=~の相返し操作は!=と!~.再呼び出し!=または!の場合、Rubyはこの2つのメソッド名を先に操作し、見つからない場合は==と=~を呼び出し、結果を返します.
 
オペレータの意味:
==等しいか
==右が左にあるかどうか
<=>生成-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 Modif(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であるかどうかにかかわらず、少なくとも1回実行されます.以下のようにします.
print "Hello
" while false begin print "Goodbye
" 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

この3つのキーワードは、コードブロックにも使用できます.
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