`
leonzhx
  • 浏览: 792510 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Containers, Blocks, and Iterators

阅读更多

1.   The class Array holds a collection of object references. Each object reference occupies a position in the array, identified by a non-negative integer index.

 

2.  A literal array is simply a list of objects between square brackets(optional).

 

3.   Arrays are indexed using the [ ] operator. As with most Ruby operators, this is actually a method (an instance method of class Array) and hence can be overridden in subclasses.

 

4.   Index an array with a negative integer, and it counts from the end.

 

5.   You can also index arrays with a pair of numbers, [start,count ]. This returns a new array consisting of references to count objects starting at position start.

 

6.   You can index arrays using ranges, in which start and end positions are separated by two or three periods. The two-period form includes the end position; the three-period form does not.

 

7.   The [ ] operator has a corresponding [ ]= operator, If used with a single integer index, the element at that position is replaced by whatever is on the right side of the assignment. Any gaps that result will be filled with nil.

a = [ 1, 3, 5, 7, 9 ] → [1, 3, 5, 7, 9]

a[1] = ’bat’ → [1, "bat", 5, 7, 9]

a[-3] = ’cat’ → [1, "bat", "cat", 7, 9]

a[3] = [ 9, 8 ] → [1, "bat", "cat", [9, 8], 9]

a[6] = 99 → [1, "bat", "cat", [9, 8], 9, nil, 99] 
 

 

8.   If the index to [ ]= is two numbers (a start and a length) or a range, then those elements in the original array are replaced by whatever is on the right side of the assignment. If the length is zero, the right side is inserted into the array before the start position; If the right side is itself an array, its elements are used in the replacement. The array size is automatically adjusted if the index selects a different number of elements than are available on the right side of the assignment:

a = [ 1, 3, 5, 7, 9 ] → [1, 3, 5, 7, 9]

a[2, 2] = ’cat’ → [1, 3, "cat", 9]

a[2, 0] = ’dog’ → [1, 3, "dog", "cat", 9]

a[1, 1] = [ 9, 8, 7 ] → [1, 9, 8, 7, "dog", "cat", 9]

a[0..3] = [] → ["dog", "cat", 9]

a[5..6] = 99, 98 → ["dog", "cat", 9, nil, nil, 99, 98] 
 

 

9.   push and pop add and remove elements from the end of an array, so you can use the array as a stack.

 

10.  unshift and shift add and remove elements from the head of an array. Combine shift and push , and you have a first-in first-out (FIFO) queue.

 

11.  The first and last methods return the n entries at the head or end of an array without removing them.

 

12.  Hashes (sometimes known as associative arrays, maps, or dictionaries) are similar to arrays in that they are indexed collections of object references. However, you can index a hash with objects of any type: symbols, strings, regular expressions, and so on.

 

13.  If we want keys to be symbols, we can write the hash literal using either the old syntax with => or the new key: value syntax:

h = { dog: 'canine', cat: 'feline', donkey: 'asinine' }

# same as...

h = { :dog => 'canine', :cat => 'feline', :donkey => 'asinine' }
 

 

14.  Ruby remembers the order in which you add items to a hash. When you subsequently iterate over the entries, Ruby will return them in that order.

 

 

15.  downcase returns a lowercase version of a string, and scan returns an array of substrings that match a given pattern:

def words_from_string(string)

  string.downcase.scan(/[\w']+/)

end
 

[\w’]+ , matches sequences containing “word characters” and single quotes.

 

 

16.  When you use sort_by , you give it a block that tells the sort what to use when making comparisons. The result of the sort is an array containing a set of two-element arrays, with each subarray corresponding to a key/entry pair in the original hash:

counts.sort_by {|word, count| count} 
 

 

17.  The method each is an iterator—a method that invokes a block of code repeatedly.

 

18.  You can think of a block as being somewhat like the body of an anonymous method. Just like a method, the block can take parameters (but, unlike a method, those parameters appear at the start of the block between vertical bars). Blocks can appear in Ruby source code only immediately after the invocation of some method.

 

19.  If there’s a variable inside a block with the same name as a variable in the same scope outside the block, the two are the same. Parameters to a block are now always local to a block, even if they have the same name as locals in the surrounding scope:

value = "some shape"

[ 1, 2 ].each {|value| puts value }

puts value
 

 

20.  You can define block-local variables by putting them after a semicolon in the block’s parameter list:

 

square = "some shape"

sum = 0

[1, 2, 3, 4].each do |value; square|

  square = value * value # this is a different variable

  sum += square

end 
 

 

21.  A Ruby iterator is simply a method that can invoke a block of code.

 

22.  A common iterator is collect (also known as map ), which takes each element from the collection and passes it to the block. The results returned by the block are used to construct a new array:

["H", "A", "L"].collect {|x| x.succ } # => ["I", "B", "M"]
 

the succ method increments a string value.

 

 

23.  The each_with_index method calls its block with two parameters: the current element of the iteration and the count (which starts at zero):

f = File.open("testfile")

f.each_with_index do |line, index|

  puts "Line #{index} is: #{line}"

end

f.close 
 

 

24.  The inject method lets you accumulate a value across the members of a collection:

[1,3,5,7].inject(0) {|sum, element| sum+element} # => 16

[1,3,5,7].inject(1) {|product, element| product*element} # => 105
 

inject works like this: the first time the associated block is called, sum is set to inject ’s parameter, and element is set to the first element in the collection. The second and subsequent times the block is called, sum is set to the value returned by the block on the previous call. The final value of inject is the value returned by the block the last time it was called. If inject is called with no parameter, it uses the first element of the collection as the initial value and starts the iteration with the second value. You can also give it the name of the method you want to apply to successive elements of the collection:

 

[1,3,5,7].inject(:+) # => 16

[1,3,5,7].inject(:*) # => 105 
 

addition and multiplication are simply methods on numbers, and :+ is the symbol corresponding to the method + .

 

 

25.  You can create an Enumerator object by calling the to_enum method (or its synonym, enum_for ) on a collection such as an array or a hash. Most of the internal iterator methods will also return an Enumerator object if called without a block:

a = [ 1, 3, "cat" ]

enum_a = a.each # create an Enumerator using an internal iterator

enum_a.next # => 1

enum_a.next # => 3
 

 

26.  Ruby has a method called loop that does nothing but repeatedly invoke its block. Typically, your code in the block will break out of the loop when some condition occurs. But loop is also smart when you use an Enumerator —when an enumerator object runs out of values inside a loop , the loop will terminate cleanly:

short_enum = [1, 2, 3].to_enum

long_enum = ('a'..'z').to_enum

loop do

  puts "#{short_enum.next} - #{long_enum.next}"

end 
 

 

27.  The each_char method of strings will return an enumerator if you don’t give it a block, and you can then call each_with_index or with_index on that enumerator:

result = []

"cat".each_char.each_with_index {|item, index| result << [item, index] }

result # => [["c", 0], ["a", 1], ["t", 2]] 
 

 

28.  You can also create the Enumerator object explicitly—create one that calls our string’s each_char method. We can call to_a on that enumerator to iterate over it:

enum = "cat".enum_for(:each_char)

enum.to_a # => ["c", "a", "t"] 
 

If the method we’re using as the basis of our enumerator takes parameters, we can pass them to enum_for :

enum_in_threes = (1..10).enum_for(:each_slice, 3)

enum_in_threes.to_a # => [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]] 
 

 

29.  You can create an explicit enumerator, passing it a block. The code in the block will be used when the enumerator object needs to supply a fresh value to your program. The block isn’t simply executed from top to bottom. Instead, the block is executed in parallel with the rest of your program’s code. Execution starts at the top and pauses when the block yields a value to your code. When the code needs the next value, execution resumes at the statement following the yield:

triangular_numbers = Enumerator.new do |yielder|

  number = 0

  count = 1

  loop do

    number += count

    count += 1

    yielder.yield number

  end

end 
 

 

30.  Enumerator objects are also enumerable (that is to say, the methods available to enumerable objects are also available to them).

 

31.  we use the &block notation to pass the block as a parameter to the method :

triangular_numbers = Enumerator.new do |yielder|

  # ... as before

end

class Enumerator

  def infinite_select(&block)

    Enumerator.new do |yielder|

      self.each do |value|

        yielder.yield(value) if block.call(value)

      end

    end

  end

end

p triangular_numbers

.infinite_select {|val| val % 10 == 0}

.infinite_select {|val| val.to_s =~ /3/ }

.first(5)
 

32.  If File.open has an associated block, then that block will be invoked with a file object, and the file will be closed when the block terminates. This is interesting, because it means that File.open has two different behaviors.When called with a block, it executes the block and closes the file.When called without a block, it returns the file object. This is made possible by the method block_given? , which returns true if a block is associated with the current method.

 

 

33.  If the last parameter in a method definition is prefixed with an ampersand (such as &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.

 

34.  Both lambda and Proc.new take a block and return an object of class Proc .

 

35.  Even though the parameter is out of scope by the time the block is called, the parameter remains accessible to the block. This is called a closure—variables in the surrounding scope that are referenced in a block remain accessible for the life of that block and the life of any Proc object created from that block:

def n_times(thing)

  lambda {|n| thing * n }

end

p1 = n_times(23) # thing is out of scope at this time, but it still works

 

36.  Rather than write this:

 

lambda { |params| ... }
 

 

you can now write the following:

 

->params { ... }

proc1 = -> arg { puts "In proc1 with #{arg}" }

proc2 = -> arg1, arg2 { puts "In proc2 with #{arg1} and #{arg2}" }

proc3 = ->(arg1, arg2) { puts "In proc3 with #{arg1} and #{arg2}" } 
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics