Learning Ruby 3, Containers, Blocks, and Iterators

with<Programming Ruby>
Chapter 4
Containers, Blocks, and Iterators

Containers

Arrays
a = [ 3.14159,  "pie", 99 ]
a.class         Array
           →
a.length        3
           →
a[0]            3.14159
           →

b = Array.new
b.length   →0
b[0] = "second"
b[1] = "array"
b          → ["second", "array"]

   Positive → 0                                         Negative
                      1     2     3     4    5      6
    indices                                        −1 ← indices
              −7     −6    −5    −4    −3   −2
          a = “ant” “bat” “cat” “dog” “elk” “?y” “gnu”
      a[2] →              “cat”
     a[-3] →                          “elk”
  a[1..3] →         “bat” “cat” “dog”
a[-3..-1] →                           “elk” “?y” “gnu”
 a[4..-2] →                           “elk” “?y”

the index to [ ]= is two numbers (a start and a length) or a range
a = [ 1, 3, 5, 7, 9 ]   [1, 3, 5, 7, 9]
                      →
a[2, 2] = ’cat’         [1, 3, "cat", 9]
                      →
                        [1, 3, "dog", "cat", 9]
a[2, 0] = ’dog’       →
                        [1, 9, 8, 7, "dog", "cat", 9]
a[1, 1] = [ 9, 8, 7 ] →
a[0..3] = []            ["dog", "cat", 9]
                      →
a[5..6] = 99, 98        ["dog", "cat", 9, nil, nil, 99, 98]
                      →

Hashes (associative arrays, maps, or dictionaries)
h = { 'dog' => 'canine', 'cat' => 'feline', 'donkey' => 'asinine' }
h.length   →3
h['dog']   → "canine"
h['cow'] = 'bovine'
h[12]    = 'dodecine'
h['cat'] = 99
h          → {"cow"=>"bovine", "cat"=>99, 12=>"dodecine",
               "donkey"=>"asinine", "dog"=>"canine"}

Implementing a SongList Container

class SongList
  def append(song)
    @songs.push(song)
    self
  end
end

class SongList
  def delete_first
    @songs.shift
  end
  def delete_last
    @songs.pop
  end
end

class SongList
  def [](index)
    @songs[index]
  end
end

Blocks and Iterators

class SongList
  def with_title(title)
    @songs.find {|song| title == song.name }
  end
end
find is an iterator
iterator: a method that invokes a block of code repeatedly
    def three_times
      yield
      yield
      yield
    end
    three_times { puts "Hello" }
produces:
    Hello
    Hello
    Hello

i1, i2 = 1, 1 # parallel assignment (i1 = 1 and i2 = 1)

def fib_up_to(max)
  i1, i2 = 1, 1        # parallel assignment (i1 = 1 and i2 = 1)
  while i1 <= max
    yield i1
    i1, i2 = i2, i1+i2
  end
end
fib_up_to(1000) {|f| print f, " " }

if they appear for the ?rst time in the block, they’re local to the block. If instead they ?rst appeared outside the block, the variables will be shared between the block and the surrounding environment.
a = [1, 2]
b = 'cat'
a.each {|b| c = b * a[1] }
a                 [1, 2]
              →
b                 2
              →
defined?(c)       nil
              →

class Array
  def find
    for i in 0…size
      value = self[i]
      return value if yield(value)
    end
    return nil
  end
end
[1, 3, 5, 7, 9].find {|v| v*v > 30 }   7
                                     →
[ 1, 3, 5, 7, 9 ].each {|i| puts i }
["H", "A", "L"].collect {|x| x.succ } ["I", "B", "M"]

[1,3,5,7].inject(0) {|sum, element| sum+element}           16
                                                         →
[1,3,5,7].inject(1) {|product, element| product*element}   105
                                                         →

if inject is called with no parameter,it uses the ?rst element of the collection as the initial value and starts the iteration with the second value.
[1,3,5,7].inject {|sum, element| sum+element}           16
                                                      →
[1,3,5,7].inject {|product, element| product*element}   105
                                                      →

Blocks for Transactions
class File
  def File.open_and_process(*args)
    f = File.open(*args)
    yield f
    f.close()
  end
end

 *args, meaning “collect the actual parameters passed to the method into an array named args

Blocks Can Be Closures

songlist = SongList.new
class JukeboxButton < Button
  def initialize(label, &action)
    super(label)
    @action = action
  end
  def button_pressed
    @action.call(self)
  end
end
start_button = JukeboxButton.new("Start") { songlist.start }
pause_button = JukeboxButton.new("Pause") { songlist.pause }
                                                                         &action,Ruby looks for a code block whenever that method is called. That code block is converted to an object of class Proc and assigned to the parameter. You can then treat the parameter as any other variable. In our example, we assigned it to the instance variable @action. When the callback method button_pressed is invoked, we use the Proc#call method on that object to invoke the block.

method lambda, which converts a block to a Proc object.

def n_times(thing)
  return lambda {|n| thing * n }
end
p1 = n_times(23)
p1.call(3)   → 69
p1.call(4)   → 92
p2 = n_times("Hello ")
p2.call(3)   → "Hello Hello Hello "

此條目發表在 Ruby on Rails 分類目錄。將固定鏈接加入收藏夾。

發表評論