===== Wypełnianie tablic zbiorem =====
* splat
>> a=*(1..10)
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
* ''Array()'' method, provided by ''Kernel'', which tries to call ''to_ary'', then ''to_a'' on its argument:
Array(1..10)
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
* ''to_a'' method
(1..10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
* ''Array.new'':
Array.new(100) {|i| i + 1}
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
===== Jak Ruby widzi mój kod? =====
$ ruby -e 'require "pp"; pp Array.new([*(1..10)])' --dump parsetree_with_comment
###########################################################
## Do NOT use this node dump for any purpose other than ##
## debug and research. Compatibility is not guaranteed. ##
###########################################################
# @ NODE_SCOPE (line: 1)
# | # new scope
# | # format: [nd_tbl]: local table, [nd_args]: arguments, [nd_body]: body
# +- nd_tbl (local table): (empty)
# +- nd_args (arguments):
# | (null node)
# +- nd_body (body):
# @ NODE_BLOCK (line: 1)
# | # statement sequence
# | # format: [nd_head]; [nd_next]
# | # example: foo; bar
# +- nd_head (current statement):
# | @ NODE_FCALL (line: 1)
# | | # function call
# | | # format: [nd_mid]([nd_args])
# | | # example: foo(1)
# | +- nd_mid (method id): :require
# | +- nd_args (arguments):
# | @ NODE_ARRAY (line: 1)
# | | # array constructor
# | | # format: [ [nd_head], [nd_next].. ] (length: [nd_alen])
# | | # example: [1, 2, 3]
# | +- nd_alen (length): 1
# | +- nd_head (element):
# | | @ NODE_STR (line: 1)
# | | | # string literal
# | | | # format: [nd_lit]
# | | | # example: 'foo'
# | | +- nd_lit (literal): "pp"
# | +- nd_next (next element):
# | (null node)
# +- nd_next (next block):
# @ NODE_BLOCK (line: 1)
# | # statement sequence
# | # format: [nd_head]; [nd_next]
# | # example: foo; bar
# +- nd_head (current statement):
# | @ NODE_FCALL (line: 1)
# | | # function call
# | | # format: [nd_mid]([nd_args])
# | | # example: foo(1)
# | +- nd_mid (method id): :pp
# | +- nd_args (arguments):
# | @ NODE_ARRAY (line: 1)
# | | # array constructor
# | | # format: [ [nd_head], [nd_next].. ] (length: [nd_alen])
# | | # example: [1, 2, 3]
# | +- nd_alen (length): 1
# | +- nd_head (element):
# | | @ NODE_CALL (line: 1)
# | | | # method invocation
# | | | # format: [nd_recv].[nd_mid]([nd_args])
# | | | # example: obj.foo(1)
# | | +- nd_mid (method id): :new
# | | +- nd_recv (receiver):
# | | | @ NODE_CONST (line: 1)
# | | | | # constant reference
# | | | | # format: [nd_vid](constant)
# | | | | # example: X
# | | | +- nd_vid (local variable): :Array
# | | +- nd_args (arguments):
# | | @ NODE_ARRAY (line: 1)
# | | | # array constructor
# | | | # format: [ [nd_head], [nd_next].. ] (length: [nd_alen])
# | | | # example: [1, 2, 3]
# | | +- nd_alen (length): 1
# | | +- nd_head (element):
# | | | @ NODE_SPLAT (line: 1)
# | | | | # splat argument
# | | | | # format: *[nd_head]
# | | | | # example: foo(*ary)
# | | | +- nd_head (splat'ed array):
# | | | @ NODE_LIT (line: 1)
# | | | | # literal
# | | | | # format: [nd_lit]
# | | | | # example: 1, /foo/
# | | | +- nd_lit (literal): 1..10
# | | +- nd_next (next element):
# | | (null node)
# | +- nd_next (next element):
# | (null node)
# +- nd_next (next block):
# (null node)
Możemy zażyczyć sobie również listę wykonywanych instrukcji przez VM:
$ ruby -e 'require "pp"; pp Array.new([*(1..10)])' --dump insns
== disasm: @-e>======================
0000 trace 1 ( 1)
0002 putself
0003 putstring "pp"
0005 send :require, 1, nil, 8,
0011 pop
0012 trace 1
0014 putself
0015 getinlinecache 22,
0018 getconstant :Array
0020 setinlinecache
0022 trace 1
0024 putobject 1..10
0026 splatarray true
0028 send :new, 1, nil, 0,
0034 send :pp, 1, nil, 8,
0040 leave
Można też zobaczyć jak działa parser Ruby'ego:
$ ruby -e 'require "pp"; pp Array.new([*(1..10)])' --dump yydebug
Starting parse
Entering state 0
Reducing stack by rule 1 (line 782):
-> $$ = nterm @1 ()
Stack now 0
Entering state 2
Reading a token: Next token is token tIDENTIFIER ()
Shifting token tIDENTIFIER ()
Entering state 35
Reading a token: Next token is token tSTRING_BEG ()
Reducing stack by rule 547 (line 4818):
$1 = token tIDENTIFIER ()
-> $$ = nterm operation ()
Stack now 0 2
Entering state 110
Next token is token tSTRING_BEG ()
[...]
===== collection.fetch =====
Za pomocą tej metody, dostępnej większości kolekcji, można spróbować pobrać dany element, a w wypadku jego braku uruchomić zdefiniowaną akcję. Gdy nie podamy bloku, podniesie wyjątek.
TODO: Napisać jakiś przykład!
# Wartość standardowa
width = options.fetch(:width) {40}
# Podnoszenie wyjątku
opt = {}.fetch(:required_opt) do
raise ArgumentError, "Missing option!"
end
===== Range#cover? =====
''Range#include?'' iteruje od ''min'' do szukanej wartości (lub ''max''). Przy dużych zasięgach, jest to bardzo wolne (''O(N)'' zamiast ''O(1)''). Możemy skorzystać z ''Range#cover?'', która porównuje tylko największą i najmniejszą wartość z szukaną wartością.
#!/usr/bin/env ruby -wKU
require 'date'
require 'benchmark'
start_date = Date.strptime('1410-07-15', '%Y-%m-%d')
end_date = Date.strptime('2012-12-12', '%Y-%m-%d')
date = Date.strptime('2011-12-12', '%Y-%m-%d')
range = (start_date..end_date)
Benchmark.bmbm do |results|
results.report("include?") { range.include?(date) }
results.report("cover?") { range.cover?(date) }
end
Output:
Rehearsal --------------------------------------------
include? 0.430000 0.010000 0.440000 ( 0.441153)
cover? 0.000000 0.000000 0.000000 ( 0.000009)
----------------------------------- total: 0.440000sec
user system total real
include? 0.430000 0.000000 0.430000 ( 0.502014)
cover? 0.000000 0.000000 0.000000 ( 0.000013)
UWAGA: http://rhnh.net/2009/08/03/range-include-in-ruby-1-9
===== Method =====
=== Wywołanie metody ===
> Project.first.method(:name).call
=> "Server Abloesung"
=== Położenie definicji metody ===
> User.last.method(:address).source_location
=> ["/home/sqbell/.rvm/gems/ruby-1.9.3-p392/gems/mongoid-3.1.4/lib/mongoid/fields.rb", 388]
===== Różnica pomiędzy ::FindIt a FindIt =====
::Rails::Engine # absolute path to the constant
Rails::Engine # path relative to the current tree level
SEE: http://stackoverflow.com/questions/10482772/rubys-double-colon-operator-usage-differences
===== Hash z default_value array =====
hsh = Hash.new([])
hsh[:a].push(1, 2, 3) # => [1, 2, 3] # So far, so good
hsh[:b].push(4, 5, 6) # => [1, 2, 3, 4, 5, 6] # LOLWUT?!
hsh[:a] # => [1, 2, 3, 4, 5, 6]
hsh[:b] # => [1, 2, 3, 4, 5, 6]
Ta sama, pusta, tablica jest przekazywana! Spróbujmy jeszcze raz:
hsh = Hash.new { [] }
hsh[:a].push(1, 2, 3) # => [1, 2, 3]
hsh[:b].push(4, 5, 6) # => [4, 5, 6]
hsh[:c] << 7 # => [7]
hsh[:a] # => []
hsh[:b] # => []
hsh[:c] # => []
Ciągle coś jest nie tak. Za każdym razem, gdy próbujemy się dostać do wartości klucza, który nie istnieje, wykonywany jest kod przekazanego bloku. Wartość dodawana jest do tablicy, ale tablica nie jest nigdzie przypisywana, więc jest czyszczona przez ''GC''.
hsh = Hash.new { |hash, key| hash[key] = [] } # => {}
hsh[:a].push(1, 2, 3) # => [1, 2, 3]
hsh[:b].push(4, 5, 6) # => [4, 5, 6]
hsh[:c] << 7 # => [7]
hsh[:a] # => [1, 2, 3]
hsh[:b] # => [4, 5, 6]
hsh[:c] # => [7]
SEE: http://stackoverflow.com/questions/2552579/ruby-method-array-not-updating-the-array-in-hash
===== Ruby i koercja =====
FIXME: http://stackoverflow.com/questions/2799571/in-ruby-how-does-coerce-actually-work
===== Complex Ruby concepts =====
===== Enumerable#slice_before, #slice_after i #slice_when =====
Druga z tych metod dodana została dopiero w Rubym 2.2.
> [1, 'a', 2, 'b', 'c', 3, 'd', 'e', 'f'].slice_before { |e| e.is_a?(Integer) }.to_a
#=> [[1, "a"], [2, "b", "c"], [3, "d", "e", "f"]]
Zamiast bloku, możemy podać argument. Będzie on porównany za pomocą ''===''. Zatem powyższy przykład można zapisać róœnież nastęþująco:
> [1, 'a', 2, 'b', 'c', 3, 'd', 'e', 'f'].slice_before(Integer).to_a
#slice_after:
> [1, 'a', 2, 'b', 'c', 3, 'd', 'e', 'f'].slice_after(Integer).to_a
#=> [[1], ["a", 2], ["b", "c", 3], ["d", "e", "f"]]
#slice_when można wykorzystać do szukania następujących po sobie liczb:
> [1, 3, 4, 5, 7, 8, 9, 10, 12].slice_when { |a, b| a + 1 != b }.to_a
#=> [[1], [3, 4, 5], [7, 8, 9, 10], [12]]