2012-01-06 7 views
6

Mi experiencia es PHP y C#, pero realmente me gustaría aprender RoR. Con ese fin, comencé a leer la documentación oficial. Tengo algunas preguntas sobre algunos ejemplos de código.Algunas preguntas simples de Ruby: iteradores, bloques y símbolos

La primera es con iteradores:

class Array 
    def inject(n) 
    each { |value| n = yield(n, value) } 
    n 
    end 

    def sum 
    inject(0) { |n, value| n + value } 
    end 

    def product 
    inject(1) { |n, value| n * value } 
    end 
end 

entiendo que yield significa "ejecutar el bloque asociado aquí." Lo que me está lanzando es la parte |value| n = de each. Los otros bloques tienen más sentido para mí, ya que parecen imitar C# lambdas estilo:

public int sum(int n, int value) 
{ 
    return Inject((n, value) => n + value); 
} 

Pero el primer ejemplo es confuso para mí.

La otra es con símbolos. ¿Cuándo querría usarlos? Y por qué no puedo hacer algo como:

class Example 
    attr_reader @member 

    # more code 
end 

Respuesta

3

En el método inject or reduce, n representa un valor acumulado; esto significa que el resultado de cada iteración se acumula en la variable n. Esto podría ser, como en su ejemplo, la suma o producto de los elementos en la matriz.

yield devuelve el resultado del bloque, que se almacena en n y se utiliza en las siguientes iteraciones. Esto es lo que hace que el resultado sea "acumulativo".

a = [ 1, 2, 3 ] 
a.sum # inject(0) { |n, v| n + v } 
# n == 0; n = 0 + 1 
# n == 1; n = 1 + 2 
# n == 3; n = 3 + 3 
=> 6 

Además, para calcular la suma que también podría haber escrito a.reduce :+. Esto funciona para cualquier operación binaria. Si su método se llama symbol, escribir a.reduce :symbol es lo mismo que escribir a.reduce { |n, v| n.symbol v }.

attr y la empresa son en realidad métodos. Bajo el capó, definen dinámicamente los métodos para ti. Utiliza el símbolo que pasó para calcular los nombres de la variable de instancia y los métodos. :member da como resultado la variable de instancia @member y los métodos member y member =.

La razón por la que no puede escribir attr_reader @member es porque @member no es un objeto en sí mismo, ni puede convertirse en un símbolo; en realidad le dice a ruby ​​que busque el valor de la variable de instancia @member del objeto self, que, en el ámbito de clase, es la clase misma.

Para ilustrar:

class Example 
    @member = :member 
    attr_accessor @member 
end 

e = Example.new 
e.member = :value 
e.member 
=> :value 

Recuerde que el acceso a las variables de instancia no se ha establecido produce nil, y dado que la familia attr método acepta solamente símbolos, se obtiene: TypeError: nil is not a symbol.

En cuanto al uso Symbol, puede tipo de usarlos como cadenas. Son excelentes claves hash porque los símbolos iguales siempre se refieren al mismo objeto, a diferencia de las cadenas.

:a.object_id == :a.object_id 
=> true 
'a'.object_id == 'a'.object_id 
=> false 

También son de uso común para referirse a los nombres de métodos y can actually be converted to Procs, que puede transmitirse a los métodos. Esto es lo que nos permite escribir cosas como array.map &:to_s.

Consulte this article para obtener más interpretaciones del símbolo.

1

Para la definición de inject, que está básicamente la creación de bloques encadenados. Específicamente, la variable n en {|value| n = yield(n, value)} es esencialmente un acumulador para el bloque pasado a inject. Entonces, por ejemplo, para la definición de product, inject(1) {|value| n * value}, supongamos que tiene una matriz my_array = [1, 2, 3, 4]. Cuando llame al my_array.product, comience por llamar al inject con n = 1. each cede al bloque definido en inject, que a su vez rinde al bloque pasado a inject con n (1) y el primer valor de la matriz (1 como bueno, en este caso). Este bloque, {|n, value| n * value}, devuelve 1 == 1 * 1, que se establece en la variable n inject. A continuación, se obtiene 2 de cada uno, y el bloque definido en el bloque inject cede como yield(1, 2), que devuelve 2 y lo asigna a n. El siguiente 3 se obtiene de each, el bloque produce los valores (2, 3) y devuelve 6, que se almacena en n para el siguiente valor, y así sucesivamente. Esencialmente, el seguimiento del valor absoluto agnóstico del cálculo que se realiza en las rutinas especializadas (sum y product) permite la generalización. Sin eso, tendrías que declarar, p.

def sum 
    n = 0 
    each {|val| n += val} 
end 

def product 
    n = 1 
    each {|val| n *= val} 
end 

que es molestamente repetitivo.

Para su segunda pregunta, attr_reader y su familia son ellos mismos métodos que están definiendo las rutinas de acceso apropiadas usando define_method internamente, en un proceso llamado metaprogramación; no son declaraciones de lenguaje, sino simplemente métodos antiguos. Estas funciones esperan pasar un símbolo (o, tal vez, una cadena) que da el nombre de los accesores que está creando. Podría, en teoría, usar variables de instancia como @member aquí, aunque sería el valor al que @member puntos se pasarían y usarían en define_method. Para obtener un ejemplo de cómo se implementan estos, this page muestra algunos ejemplos de métodos attr_ *.

1
def inject(accumulator) 
    each { |value| accumulator = yield(accumulator, value) } 
    accumulator 
end 

esto es sólo produciendo el valor actual de accumulator y el elemento de la matriz a de inyectar bloque y luego almacenar el resultado en el acumulador de nuevo.

class Example 
    attr_reader @member 
end 

attr_reader es sólo un método cuyo argumento es el nombre del descriptor de acceso que desea configurar.Por lo tanto, de una manera artificiosa que podría hacer

class Example 
    @ivar_name = 'foo' 
    attr_reader @ivar_name 
end 

para crear un método getter llamada foo

1

Su confusión con el primer ejemplo puede deberse a su lectura |value| n como una sola expresión, pero no lo es.

Esta versión con el formato podría ser más claro para usted:

def inject(n) 
    each do |value| 
    n = yield(n, value) 
    end 
    return n 
end 

value es un elemento de la matriz, y se produjo con n a lo que se pasa el bloque de inject, el resultado de la cual se establece en n .Si no está claro, lee en el método each, que toma un bloque y cede cada elemento de la matriz. Entonces debería ser más claro cómo funciona la acumulación.

attr_reader es menos extraño si se tiene en cuenta que se trata de un método para generar métodos de acceso. No es un accesorio en sí mismo. No necesita tratar el valor de la variable @member, solo su nombre. :member es solo la versión interna de la cadena 'miembro', que es el nombre de la variable.

Puede pensar en símbolos como cadenas más ligeras, con la ventaja adicional de que cada etiqueta igual es el mismo objeto - :foo.object_id == :foo.object_id, mientras que 'foo'.object_id != 'foo'.object_id, porque cada 'foo' es un objeto nuevo. Puede intentarlo por usted mismo en irb. Piense en ellos como etiquetas o cuerdas primitivas. Son sorprendentemente útiles y surgen mucho, p. para la metaprogramación o como claves en hashes. Como se ha señalado en otro lugar, llamando object.send :foo es lo mismo que llamar object.foo

Es probablemente vale la pena leer algunos capítulos tempranos de la 'pickaxe' book para aprender un poco más de rubí, que le ayudará a entender y apreciar los rieles de material extra añade.

0

Primero debe saber dónde usar los símbolos y dónde no está .. El símbolo se usa especialmente para representar algo. Ej:: nombre,: edad así. Aquí no vamos a realizar ninguna operación usando esto. Las cadenas se utilizan solo para el procesamiento de datos. Ej: 'a = nombre'. Aquí voy a usar esta variable 'a' aún más para otras operaciones de cuerda en ruby. Además, el símbolo es más eficiente en cuanto a la memoria que las cadenas y es inmutable. Es por eso que los desarrolladores de ruby ​​prefieren símbolos que cadenas.

Incluso puede usar el método de inyección para calcular la suma como (1..5) .to_a.inject (: +)

Cuestiones relacionadas