ruby language-agnostic lambda

ruby - ¿Dónde y cuándo usar Lambda?



language-agnostic (7)

Estoy tratando de entender por qué realmente necesitamos lambda o proc en ruby ​​(o en cualquier otro idioma).

#method def add a,b c = a+b end #using proc def add_proc a,b f = Proc.new {|x,y| x + y } f.call a,b end #using lambda function def add_lambda a,b f = lambda {|x,y| x + y} f.call a,b end puts add 1,1 puts add_proc 1,2 puts add_lambda 1,3

Puedo hacer una adición simple usando: 1. función normal def, 2. usando proc y 3. usando lambda.

¿Pero por qué y dónde usar lambda en el mundo real? Cualquier ejemplo donde las funciones no puedan ser usadas y lambda debería ser usado.


Los bloques son más o menos lo mismo

Bueno, en Ruby, uno no suele usar lambda o proc, porque los bloques son más o menos lo mismo y mucho más conveniente.

Los usos son infinitos, pero podemos enumerar algunos casos típicos. Uno normalmente piensa en las funciones como bloques de nivel inferior que realizan una parte del proceso, tal vez escritos en general y convertidos en una biblioteca.

Pero a menudo uno quiere automatizar el contenedor y proporcionar una biblioteca personalizada. Imagine una función que hace una conexión HTTP o HTTPS, o una conexión TCP directa, alimenta la E / S a su cliente y luego cierra la conexión. O tal vez simplemente haga lo mismo con un viejo archivo simple.

Entonces, en Ruby pondríamos la función en una biblioteca y le llevaremos un bloque para que el usuario ... el cliente ... el "que llama" escriba su lógica de aplicación.

En otro idioma, esto debería hacerse con una clase que implemente una interfaz o un puntero de función. Ruby tiene bloques, pero todos son ejemplos de un patrón de diseño de estilo lambda.


Encontré esto útil para entender las diferencias:

http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/

Pero, en general, el punto es que a veces escribes un método, pero no sabes lo que vas a querer hacer en un determinado punto de ese método, por lo que dejas que la persona que llama lo decida.

P.ej:

def iterate_over_two_arrays(arr1, arr2, the_proc) arr1.each do |x| arr2.each do |y| # ok I''m iterating over two arrays, but I could do lots of useful things now # so I''ll leave it up to the caller to decide by passing in a proc the_proc.call(x,y) end end end

Luego, en lugar de escribir un método iterate_over_two_arrays_and_print_sum y un método iterate_over_two_arrays_and_print_product , simplemente llame:

iterate_over_two_arrays([1,2,3], [4,5,6], Proc.new {|x,y| puts x + y }

o

iterate_over_two_arrays([1,2,3], [4,5,6], Proc.new {|x,y| puts x * y }

entonces es mas flexible


Es cierto, no necesitas funciones anónimas (o lambdas, o como quieras llamarlas). Pero hay muchas cosas que no necesitas . No necesita clases: simplemente transfiera todas las variables de instancia a las funciones normales. Entonces

class Foo attr_accessor :bar, :baz def frob(x) bar = baz*x end end

se convertiría

def new_Foo(bar,baz) [bar,baz] end def bar(foo) foo[0] end # Other attribute accessors stripped for brevity''s sake def frob(foo,x) foo[0] = foo[1]*x end

De manera similar, no necesita ningún bucle excepto el loop...end con if y break . Podría seguir y seguir. 1 Pero desea programar con clases en Ruby. Desea poder utilizar while loops, o incluso array.each { |x| ... } array.each { |x| ... } , y desea poder utilizar a unless lugar de if not .

Al igual que estas características, las funciones anónimas están ahí para ayudarlo a expresar las cosas de manera elegante, concisa y sensata. Poder escribir some_function(lambda { |x,y| x + f(y) }) es mucho mejor que tener que escribir

def temp(x,y) x + f(y) end some_function temp

Es mucho más voluminoso tener que interrumpir el flujo de código para escribir una función de alimentación def , que luego debe recibir un nombre inútil, cuando es tan claro escribir la operación en línea. Es cierto que no debes usar lambda en ninguna parte, pero hay muchos lugares en los que prefiero usar una lambda.

Ruby resuelve muchos de los casos de uso de lambda con bloques: todas las funciones como each , map y open que pueden tomar un bloque como argumento básicamente toman una función anónima especial. array.map { |x| f(x) + g(x) } array.map { |x| f(x) + g(x) } es lo mismo que array.map(&lambda { |x| f(x) + g(x) }) (donde & hace que la lambda sea "especial" de la misma manera que el bloque desnudo es). De nuevo, podría escribir una función de alimentación por separado cada vez, pero ¿por qué querría hacerlo?

Los lenguajes distintos de Ruby que admiten ese estilo de programación no tienen bloques, pero a menudo admiten una sintaxis lambda de peso ligero, como Haskell''s /x -> fx + gx , o C # ''s x => f(x) + g(x); 2 . Cada vez que tengo una función que necesita tomar un comportamiento abstracto, como map , o each , o on_clicked , voy a estar agradecido por la capacidad de pasar una lambda en lugar de una función con nombre, porque es mucho más fácil . Finalmente, dejas de pensar en ellos como algo especial: son tan emocionantes como la sintaxis literal para las matrices en lugar de empty().append(1).append(2).append(3) . Solo otra parte útil del lenguaje.

1: en el caso degenerado, solo necesita ocho instrucciones : +-<>[]., . <> mueve un "puntero" imaginario a lo largo de una matriz; +- incrementa y disminuye el entero en la celda actual; [] realizar un ciclo while non-zero; y ., hacer entrada y salida. De hecho, solo necesita una sola instrucción , como subleq abc (restar a de b y saltar a c si el resultado es menor o igual a cero).

2: nunca he usado C #, así que si esa sintaxis es incorrecta, puedes corregirla.


Se usan como funciones de "orden superior". Básicamente, para los casos en los que pasa una función a otra, de modo que la función receptora pueda llamar a la función transferida de acuerdo con su propia lógica.

Esto es común en Ruby para la iteración, por ejemplo, some_list.each {| item | ...} para hacer algo con each item de some_list . Aunque tenga en cuenta que no usamos la palabra clave lambda ; como se señaló, un bloqueo es básicamente lo mismo.

En Python (ya que tenemos una etiqueta language-agnostic del idioma en esta pregunta) no se puede escribir nada como un bloque de Ruby, por lo que la palabra clave lambda aparece con más frecuencia. Sin embargo, puede obtener un efecto de "acceso directo" similar a partir de las comprensiones de la lista y las expresiones del generador.


1) Es solo una conveniencia. No necesita nombrar ciertos bloques

special_sort(array, :compare_proc => lambda { |left, right| left.special_param <=> right.special_param }

(Imagínese si tuviera que nombrar todos estos bloques)

2) #lambda se usa generalmente para crear clojures:

def generate_multiple_proc(cofactor) lambda { |element| element * cofactor } end [1, 2, 3, 4].map(&generate_multiple_proc(2)) # => [2, 3, 5, 8] [1, 2, 3, 4].map(&generate_multiple_proc(3)) # => [3, 6, 9, 12]


Todo se reduce al estilo. Las lambdas son un estilo declarativo, los métodos son un estilo imperativo. Considera esto:

Lambda, bloques, procs, son todos tipos diferentes de cierre. Ahora la pregunta es cuándo y por qué utilizar un cierre anónimo. Puedo responder eso, al menos en ruby!

Los cierres contienen el contexto léxico de donde fueron llamados. Si llama a un método desde dentro de un método, no obtiene el contexto de donde se llamó el método. Esto se debe a la forma en que se almacena la cadena de objetos en el AST.

Un cierre (lambda) por otro lado, se puede pasar CON contexto léxico a través de un método, lo que permite una evaluación lenta.

También las lambdas se prestan naturalmente a la recursión y la enumeración.