Ruby para programadores Python

Diferencias que voy encontrando entre los dos lenguajes y me llaman la atencion.

Juanjo Conti

@jjconti

http://www.juanjoconti.com.ar

In [1]:
File.open('proruby.jpg')
Out[1]:
In [2]:
RUBY_VERSION
Out[2]:
"2.1.2"

¿Qué muestro?

  • ### Ejemplos con tipos de datos (bools, strings, arrays, ranges, symbols, hashes)

  • ### Introducción a bloques

  • ### Ejemplos con métodos

  • ### Cuestiones sintácticas del lenguaje

  • ### Advertencias!

Ejemplos con tipos de datos

Todo tiene valor de verdad true excepto false y nil

In [3]:
def que_es obj
  obj ? "#{obj} es true" : "#{obj} es false"
end

que_es []
Out[3]:
"[] es true"
In [4]:
que_es 1
Out[4]:
"1 es true"
In [5]:
que_es false
Out[5]:
"false es false"

Los strings son mutables

In [6]:
s = "Cactus"
s[0] = 'KKKK'
s
Out[6]:
"KKKKactus"

Conversión de tipos mediante métodos

In [7]:
"hola".to_i
Out[7]:
0
In [8]:
".5".to_f
Out[8]:
0.5
In [9]:
" 1.5    -  fffffff ".to_f
Out[9]:
1.5
In [10]:
"..... 1.54    -  fffffff ".to_f
Out[10]:
0.0

Arrays y sus índices

In [11]:
a = []
a[4] = 1
a
Out[11]:
[nil, nil, nil, nil, 1]
In [12]:
a[10]

Objetos mutables como valor por defecto en un array

In [13]:
arr = Array.new(3, {})
Out[13]:
[{}, {}, {}]
In [14]:
arr[1][:a] = 1
arr
Out[14]:
[{:a=>1}, {:a=>1}, {:a=>1}]

Array * string

In [15]:
array = [1, 2, 3, 4, 100]
array * 2
Out[15]:
[1, 2, 3, 4, 100, 1, 2, 3, 4, 100]
In [16]:
array * '|'
Out[16]:
"1|2|3|4|100"
In [17]:
[1, [2,3], 4, [[[5]]]] * ','
Out[17]:
"1,2,3,4,5"

Los hashes recuerdan el orden de inserción

In [18]:
h = {}
h[1] = "a"
h[2] = "b"
h[3] = "c"
h.delete(1)
h[1] = "A"
h.keys
Out[18]:
[2, 3, 1]

Se pueden usar objetos mutables como claves

In [19]:
a = []
h = {a => 2}
h.default = 'defecto'
a << 1
h
Out[19]:
{[1]=>2}
In [20]:
h[a]
Out[20]:
"defecto"
In [21]:
h.rehash
h[a]
Out[21]:
2

Tratamiento especial de strings al usarlos como claves

A pesar de que son objetos mutables, al usarlos como claves, se realiza una copia de ellos y se freeza.

In [22]:
key = 'a'
hash = {key => 1}
Out[22]:
{"a"=>1}
In [23]:
key.upcase!
Out[23]:
"A"
In [24]:
hash
Out[24]:
{"a"=>1}
In [25]:
key.object_id
Out[25]:
70262201808180
In [26]:
hash.keys[0].object_id
Out[26]:
70262201808140

Notación compacta para hashes con símbolos como claves

In [27]:
{:a => 1, :b => 2}
Out[27]:
{:a=>1, :b=>2}
In [28]:
{a: 1, b: 2}
Out[28]:
{:a=>1, :b=>2}

Símbolos

In [29]:
:simbolo
Out[29]:
:simbolo
In [30]:
:"Con espacios"
Out[30]:
:"Con espacios"
In [31]:
"hola".object_id
Out[31]:
70262217568780
In [32]:
"hola".object_id
Out[32]:
70262213790460
In [33]:
:hola.object_id
Out[33]:
1171208
In [34]:
:hola.object_id
Out[34]:
1171208

Ranges

In [35]:
(1..4).to_a
Out[35]:
[1, 2, 3, 4]
In [36]:
(1...4).to_a
Out[36]:
[1, 2, 3]
In [37]:
e = "a".."z"
e.to_enum.next.next
Out[37]:
"b"

Introducción a bloques

In [38]:
def llama_bloque
    puts "inicio llama_bloque"
    yield
    puts "fin llama_bloque"
end
Out[38]:
:llama_bloque
In [39]:
llama_bloque { puts "en el bloque" }
inicio llama_bloque
en el bloque
fin llama_bloque
In [40]:
def llama_bloque
    puts "inicio llama_bloque"
    yield 2
    puts "fin llama_bloque"
end
Out[40]:
:llama_bloque
In [41]:
llama_bloque { |x| puts "en el bloque #{x}" }
inicio llama_bloque
en el bloque 2
fin llama_bloque
In [42]:
def masticar numeros
    for x in numeros
        yield x * 2
    end
end
Out[42]:
:masticar
In [43]:
masticar [1,2,3,4] { |y| y / 2 }
Out[43]:
[1, 2, 3, 4]

Ejemplos con métodos

chop vs. chomp

In [44]:
"hola".chop
Out[44]:
"hol"
In [45]:
"hola".chomp
Out[45]:
"hola"
In [46]:
"hola\n".chomp
Out[46]:
"hola"

map y collect son alias

In [47]:
[1, 2, 3].map {|x| x * 2}
Out[47]:
[2, 4, 6]
In [48]:
[1, 2, 3].collect do |x| 
    n = 1 + 1
    x * n
end
Out[48]:
[2, 4, 6]

count, size y lenght

In [49]:
array = ['hola', 3, Object.new]
array.size
Out[49]:
3
In [50]:
array.count
Out[50]:
3
In [51]:
array.length
Out[51]:
3
In [52]:
array[10] = 1
array
Out[52]:
["hola", 3, #<Object:0x007fce632a56a0>, nil, nil, nil, nil, nil, nil, nil, 1]
In [53]:
[array.size, array.count, array.length]
Out[53]:
[11, 11, 11]

to_s o to_str, to_i o to_int

In [54]:
[1.to_s, "5".to_i]
Out[54]:
["1", 5]
In [55]:
"1".to_int
NoMethodError: undefined method `to_int' for "1":String
(pry):95:in `<main>'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:355:in `eval'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:355:in `evaluate_ruby'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:323:in `handle_line'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:243:in `block (2 levels) in eval'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:242:in `catch'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:242:in `block in eval'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:241:in `catch'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:241:in `eval'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/iruby-0.1.13/lib/iruby/backend.rb:28:in `eval'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/iruby-0.1.13/lib/iruby/kernel.rb:110:in `execute_request'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/iruby-0.1.13/lib/iruby/kernel.rb:62:in `run'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/iruby-0.1.13/lib/iruby/command.rb:30:in `run_kernel'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/iruby-0.1.13/lib/iruby/command.rb:16:in `run'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/iruby-0.1.13/bin/iruby:6:in `<top (required)>'
/Users/juanjo/.rvm/gems/ruby-2.1.2/bin/iruby:23:in `load'
/Users/juanjo/.rvm/gems/ruby-2.1.2/bin/iruby:23:in `<main>'
/Users/juanjo/.rvm/gems/ruby-2.1.2/bin/ruby_executable_hooks:15:in `eval'
/Users/juanjo/.rvm/gems/ruby-2.1.2/bin/ruby_executable_hooks:15:in `<main>'
In [56]:
class MyInt
  def to_int
    0
  end
end

[1, 2][MyInt.new]
Out[56]:
1

Symbol#to_proc

In [57]:
['un', 'dos', 'tres'].map(&:upcase)
Out[57]:
["UN", "DOS", "TRES"]
In [58]:
class Symbol
  def to_proc
    Proc.new {|x| x * 2}
  end
end

['un', 'dos', 'tres'].map(&:upcase)
Out[58]:
["unun", "dosdos", "trestres"]

Cuestiones sintácticas

Sintaxis: bloques: { } o do/end

La regla de estilo dice que se recomiendo usar llaves cuando el bloque es de una línea y do/end cuando es multilínea. Para la elección, también conviene saber que las llaves tienen alta presedencia mientras que do/end baja.

In [59]:
def come_bloques a
  puts a
  yield
end
Out[59]:
:come_bloques
In [60]:
come_bloques 1 { puts 2}
SyntaxError: unexpected '{', expecting end-of-input
come_bloques 1 { puts 2}
                ^
In [61]:
come_bloques(1) { puts 2}
1
2
In [62]:
come_bloques 1 do puts 2 end
1
2

Sintaxis: el parser ve aunque no ejecute

Para saber que existe una variable, no necesita haber ejecutado su asginación. Alcanza con que el parser la haya visto.

In [63]:
q.nil?
NameError: undefined local variable or method `q' for main:Object
(pry):117:in `<main>'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:355:in `eval'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:355:in `evaluate_ruby'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:323:in `handle_line'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:243:in `block (2 levels) in eval'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:242:in `catch'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:242:in `block in eval'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:241:in `catch'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:241:in `eval'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/iruby-0.1.13/lib/iruby/backend.rb:28:in `eval'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/iruby-0.1.13/lib/iruby/kernel.rb:110:in `execute_request'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/iruby-0.1.13/lib/iruby/kernel.rb:62:in `run'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/iruby-0.1.13/lib/iruby/command.rb:30:in `run_kernel'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/iruby-0.1.13/lib/iruby/command.rb:16:in `run'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/iruby-0.1.13/bin/iruby:6:in `<top (required)>'
/Users/juanjo/.rvm/gems/ruby-2.1.2/bin/iruby:23:in `load'
/Users/juanjo/.rvm/gems/ruby-2.1.2/bin/iruby:23:in `<main>'
/Users/juanjo/.rvm/gems/ruby-2.1.2/bin/ruby_executable_hooks:15:in `eval'
/Users/juanjo/.rvm/gems/ruby-2.1.2/bin/ruby_executable_hooks:15:in `<main>'
In [64]:
q = 1 if q.nil?
Out[64]:
1
In [65]:
if false
  f = 1
end
f.inspect
Out[65]:
"nil"

Sintaxis: parámetros con valores por defecto referenciando parámetros previos

In [66]:
def parametros a, b=a*2
  [a, b]
end

parametros 10
Out[66]:
[10, 20]

Sintaxis: captura genérica de parámetros no necesariamente al final

In [67]:
def ejemplo(a, *b, c)
    puts [a, b, c]
end

ejemplo 1, 2, 3, 4, 5
[1, [2, 3, 4], 5]

Sintaxis: expresion if condición

In [68]:
a = 1 if true
Out[68]:
1

Como if es también una expresión, lo siguiente es válido:

In [69]:
if true
  puts "no no no no"
end if false

Advertencias!

Comas al final de una línea (!)

In [70]:
config = {
  A1: 1,
  B2: 2
}
Out[70]:
{:A1=>1, :B2=>2}
In [71]:
A1 = 1,
B2 = 2
A1
Out[71]:
[1, 2]

Espacios en blanco (!)

In [72]:
C1 = 'A'
C2 = 'B'
C1 + C2
Out[72]:
"AB"
In [73]:
'A' +C2
Out[73]:
"AB"
In [74]:
C1 +C2
NoMethodError: undefined method `+@' for "B":String
(pry):148:in `<main>'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:355:in `eval'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:355:in `evaluate_ruby'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:323:in `handle_line'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:243:in `block (2 levels) in eval'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:242:in `catch'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:242:in `block in eval'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:241:in `catch'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/pry-0.10.1/lib/pry/pry_instance.rb:241:in `eval'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/iruby-0.1.13/lib/iruby/backend.rb:28:in `eval'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/iruby-0.1.13/lib/iruby/kernel.rb:110:in `execute_request'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/iruby-0.1.13/lib/iruby/kernel.rb:62:in `run'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/iruby-0.1.13/lib/iruby/command.rb:30:in `run_kernel'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/iruby-0.1.13/lib/iruby/command.rb:16:in `run'
/Users/juanjo/.rvm/gems/ruby-2.1.2/gems/iruby-0.1.13/bin/iruby:6:in `<top (required)>'
/Users/juanjo/.rvm/gems/ruby-2.1.2/bin/iruby:23:in `load'
/Users/juanjo/.rvm/gems/ruby-2.1.2/bin/iruby:23:in `<main>'
/Users/juanjo/.rvm/gems/ruby-2.1.2/bin/ruby_executable_hooks:15:in `eval'
/Users/juanjo/.rvm/gems/ruby-2.1.2/bin/ruby_executable_hooks:15:in `<main>'

No uso de paréntesis (!)

El uso de paréntesis al llamar a métodos sirve no solo para aumentar la legibilidad del código sino también para ayudar al parser a tomar decisiones correctas en situaciones ambiguas.

Respuesta de Matthew Kerwin en ruby-talk.

In [75]:
def C(*args) [:C, *args]; end
C = 99
Out[75]:
99
In [76]:
C +1
Out[76]:
[:C, 1]
In [77]:
C + 1
Out[77]:
100

No vimos

Bloques, Procs y Lambdas

Clases y accesores

Excepciones

Módulos y Mixins

FIN

Muchas gracias!

¿Preguntas?

  • ## ¿Qué usaste para hacer las slides?
  • ### IPython (ahora llamado Jupyter) con el kernel IRuby y el plugin live_reveal (reveal.js)
  • ## ¿De dónde se pueden bajar las slides?
  • ### juanjoconti.com.ar
  • ### github.com/jjconti