rails porcentaje operador importar for entrada diferente datos clases bucle asignacion ruby-on-rails ruby syntactic-sugar

ruby-on-rails - operador - porcentaje en ruby



Primer indicador mágico y último en un bucle en Ruby/Rails? (16)

¿Qué pasaría si pudieras hacer esto?

%w(a b c d).each.with_position do |e, position| p [e, position] # => ["a", :first] # => ["b", :middle] # => ["c", :middle] # => ["d", :last] end

¿O esto?

%w(a, b, c, d).each_with_index.with_position do |(e, index), position| p [e, index, position] # => ["a,", 0, :first] # => ["b,", 1, :middle] # => ["c,", 2, :middle] # => ["d", 3, :last] end

En MRI> = 1.8.7, todo lo que se necesita es este parche de mono:

class Enumerable::Enumerator def with_position(&block) state = :init e = nil begin e_last = e e = self.next case state when :init state = :first when :first block.call(e_last, :first) state = :middle when :middle block.call(e_last, :middle) end rescue StopIteration case state when :first block.call(e_last, :first) when :middle block.call(e_last, :last) end return end while true end end

Tiene un pequeño motor de estado porque debe mirar hacia adelante una iteración.

El truco es que cada uno, cada_con_index, & c. devuelve un Enumerador si no tiene bloque. Los enumeradores hacen todo lo que hace un Enumerable y un poco más. Pero para nosotros, lo importante es que podemos aplicar un parche a Enumerator para agregar una forma más de iterar, "envolviendo" la iteración existente, sea lo que sea.

Ruby / Rails hace muchas cosas geniales cuando se trata de azúcar para cosas básicas, y creo que hay un escenario muy común en el que me pregunto si alguien ha hecho alguna ayuda o algo similar.

a = Array.new(5, 1) a.each_with_index do |x, i| if i == 0 print x+1 elsif i == (a.length - 1) print x*10 else print x end end

Perdonen la fealdad, pero esto llega a lo que uno podría desear ... ¿hay alguna manera de hacer algo con Ruby en el primer y último ciclo?

[EDIT] Creo que idealmente sería una extensión en Array con parámetros (instancia de matriz, función de todos los elementos, función de los primeros elementos, función de los últimos elementos) ... pero estoy abierto a otros pensamientos.


A veces un bucle for es solo tu mejor opción

if(array.count > 0) first= array[0] #... do something with the first cx = array.count -2 #so we skip the last record on a 0 based array for x in 1..cx middle = array[x] #... do something to the middle end last = array[array.count-1] #... do something with the last item. end

Sé que esta pregunta fue respondida, pero este método no tiene efectos secundarios y no verifica si el registro 13º, 14º, 15º ... 10º.OOO, 10.001º es el primer registro, o el último.

Las respuestas anteriores habrían fallado la asignación en cualquier clase de estructura de datos.


BESO

arr.each.with_index do |obj, index| p ''first'' if index == 0 p ''last'' if index == arr.count-1 end


Como muchos han señalado, each_with_index parece ser la clave para esto. Tengo este bloque de código que me gustó.

array.each_with_index do |item,index| if index == 0 # first item elsif index == array.length-1 # last item else # middle items end # all items end

O

array.each_with_index do |item,index| if index == 0 # first item end # all items if index == array.length-1 # last item end end

O por extensiones Array

class Array def each_with_position array.each_with_index do |item,index| if index == 0 yield item, :first elsif index == array.length-1 yield item, :last else yield item, :middle end end end def each_with_index_and_position array.each_with_index do |item,index| if index == 0 yield item, index, :first elsif index == array.length-1 yield item, index, :last else yield item, index, :middle end end end def each_with_position_and_index array.each_with_index do |item,index| if index == 0 yield item, :first, index elsif index == array.length-1 yield item, :last, index else yield item, :middle, index end end end end


Necesitaba esta funcionalidad de vez en cuando, así que diseñé una pequeña clase para ese propósito.

La última versión está en: https://gist.github.com/3823837

Muestra:

("a".."m").to_a.each_pos do |e| puts "Char/tfirst?/tlast?/tprev/tnext/twrapped?/tindex/tposition" if e.first? print "#{e.item}/t" print "#{e.first?}/t" print "#{e.last?}/t" print "#{e.prev}/t" print "#{e.next}/t" print "#{e.wrapped?}/t/t" print "#{e.index}/t" puts "#{e.position}/t" end # Char first? last? prev next wrapped? index position # a true false b false 0 1 # b false false a c true 1 2 # c false false b d true 2 3 # d false false c e true 3 4 # e false false d f true 4 5 # f false false e g true 5 6 # g false false f h true 6 7 # h false false g i true 7 8 # i false false h j true 8 9 # j false false i k true 9 10 # k false false j l true 10 11 # l false false k m true 11 12 # m false true l false 12 13 { a: "0", b: "1", c: "2", d: "3", e: "4", f: "5", g: "6", h: "7", i: "8", j: "9", k: "10", l: "11", m: "12", }.each_pos do |(k, v), e| puts "KV/tChar/t/tfirst?/tlast?/tprev/t/tnext/t/twrapped?/tindex/tposition" if e.first? print "#{k} => #{v}/t" print "#{e.item}/t" print "#{e.first?}/t" print "#{e.last?}/t" print "#{e.prev || "/t"}/t" print "#{e.next || "/t"}/t" print "#{e.wrapped?}/t/t" print "#{e.index}/t" puts "#{e.position}/t" end # KV Char first? last? prev next wrapped? index position # a => 0 [:a, "0"] true false [:b, "1"] false 0 1 # b => 1 [:b, "1"] false false [:a, "0"] [:c, "2"] true 1 2 # c => 2 [:c, "2"] false false [:b, "1"] [:d, "3"] true 2 3 # d => 3 [:d, "3"] false false [:c, "2"] [:e, "4"] true 3 4 # e => 4 [:e, "4"] false false [:d, "3"] [:f, "5"] true 4 5 # f => 5 [:f, "5"] false false [:e, "4"] [:g, "6"] true 5 6 # g => 6 [:g, "6"] false false [:f, "5"] [:h, "7"] true 6 7 # h => 7 [:h, "7"] false false [:g, "6"] [:i, "8"] true 7 8 # i => 8 [:i, "8"] false false [:h, "7"] [:j, "9"] true 8 9 # j => 9 [:j, "9"] false false [:i, "8"] [:k, "10"] true 9 10 # k => 10 [:k, "10"] false false [:j, "9"] [:l, "11"] true 10 11 # l => 11 [:l, "11"] false false [:k, "10"] [:m, "12"] true 11 12 # m => 12 [:m, "12"] false true [:l, "11"] false 12 13

Clase real:

module Enumerable # your each_with_position method def each_pos &block EachWithPosition.each(self, &block) end end class EachWithPosition attr_reader :index class << self def each *a, &b handler = self.new(*a, :each, &b) end end def initialize collection, method, &block @index = 0 @item, @prev, @next = nil @collection = collection @callback = block self.send(method) end def count @collection.count end alias_method :length, :count alias_method :size, :count def rest count - position end def first? @index == 0 end def last? @index == (count - 1) end def wrapped? !first? && !last? end alias_method :inner?, :wrapped? def position @index + 1 end def prev @prev end def next @next end def current @item end alias_method :item, :current alias_method :value, :current def call if @callback.arity == 1 @callback.call(self) else @callback.call(@item, self) end end def each @collection.each_cons(2) do |e, n| @prev = @item @item = e @next = n self.call @index += 1 # fix cons slice behaviour if last? @prev, @item, @next = @item, @next, nil self.call @index += 1 end end end end


No existe la sintaxis "do this the time (first | last) time" en Ruby. Pero si buscas concisión, podrías hacer esto:

a.each_with_index do |x, i| print (i > 0 ? (i == a.length - 1 ? x*10 : x) : x+1) end

El resultado es lo que esperarías:

irb(main):001:0> a = Array.new(5,1) => [1, 1, 1, 1, 1] irb(main):002:0> a.each_with_index do |x,i| irb(main):003:1* puts (i > 0 ? (i == a.length - 1 ? x*10 : x) : x+1) irb(main):004:1> end 2 1 1 1 10


No pude resistir :) Esto no está ajustado para el rendimiento, aunque supongo que no debería ser mucho más lento que la mayoría de las otras respuestas aquí. ¡Todo se trata del azúcar!

class Array class EachDSL attr_accessor :idx, :max def initialize arr self.max = arr.size end def pos idx + 1 end def inside? range range.include? pos end def nth? i pos == i end def first? nth? 1 end def middle? not first? and not last? end def last? nth? max end def inside range yield if inside? range end def nth i yield if nth? i end def first yield if first? end def middle yield if middle? end def last yield if last? end end def each2 &block dsl = EachDSL.new self each_with_index do |x,i| dsl.idx = i dsl.instance_exec x, &block end end end

Ejemplo 1:

[1,2,3,4,5].each2 do |x| puts "#{x} is first" if first? puts "#{x} is third" if nth? 3 puts "#{x} is middle" if middle? puts "#{x} is last" if last? puts end # 1 is first # # 2 is middle # # 3 is third # 3 is middle # # 4 is middle # # 5 is last

Ejemplo 2:

%w{some short simple words}.each2 do |x| first do puts "#{x} is first" end inside 2..3 do puts "#{x} is second or third" end middle do puts "#{x} is middle" end last do puts "#{x} is last" end end # some is first # short is second or third # short is middle # simple is second or third # simple is middle # words is last


O un pequeño pequeño lenguaje específico del dominio:

a = [1, 2, 3, 4] FirstMiddleLast.iterate(a) do first do |e| p [e, ''first''] end middle do |e| p [e, ''middle''] end last do |e| p [e, ''last''] end end # => [1, "first"] # => [2, "middle"] # => [3, "middle"] # => [4, "last"]

y el código que lo hace funcionar:

class FirstMiddleLast def self.iterate(array, &block) fml = FirstMiddleLast.new(array) fml.instance_eval(&block) fml.iterate end attr_reader :first, :middle, :last def initialize(array) @array = array end def first(&block) @first = block end def middle(&block) @middle = block end def last(&block) @last = block end def iterate @first.call(@array.first) unless @array.empty? if @array.size > 1 @array[1..-2].each do |e| @middle.call(e) end @last.call(@array.last) end end end

Empecé a pensar, "si solo pudieras pasar múltiples bloques a una función de Ruby, entonces podrías tener una solución fácil y lisa para esta pregunta". Luego me di cuenta de que los DSL juegan pequeños trucos que son casi como pasar múltiples bloques.


Particione la matriz en rangos donde se supone que los elementos dentro de cada rango se comporten de manera diferente. Asigne cada rango así creado a un bloque.

class PartitionEnumerator include RangeMaker def initialize(array) @array = array @handlers = {} end def add(range, handler) @handlers[range] = handler end def iterate @handlers.each_pair do |range, handler| @array[range].each { |value| puts handler.call(value) } end end end

Podría crear rangos a mano, pero estos ayudantes a continuación lo hacen más fácil:

module RangeMaker def create_range(s) last_index = @array.size - 1 indexes = (0..last_index) return (indexes.first..indexes.first) if s == :first return (indexes.second..indexes.second_last) if s == :middle return (indexes.last..indexes.last) if s == :last end end class Range def second self.first + 1 end def second_last self.last - 1 end end

Uso:

a = [1, 2, 3, 4, 5, 6] e = PartitionEnumerator.new(a) e.add(e.create_range(:first), Proc.new { |x| x + 1 } ) e.add(e.create_range(:middle), Proc.new { |x| x * 10 } ) e.add(e.create_range(:last), Proc.new { |x| x } ) e.iterate


Podría tomar los primeros y últimos elementos y procesarlos de manera diferente, si lo desea.

first = array.shift last = array.pop process_first_one array.each { |x| process_middle_bits } process_last_one


Pregunta interesante, y una que he pensado un poco también.

Creo que tendrías que crear tres bloques / procs / lo que se llame, y luego crear un método que llame al bloque / proc / lo que sea correcto. (Perdón por la imprecisión - todavía no soy un metaprogramador de cinturón negro) [ Editar : sin embargo, he copiado de alguien que está en la parte inferior)

class FancyArray def initialize(array) @boring_array = array @first_code = nil @main_code = nil @last_code = nil end def set_first_code(&code) @first_code = code end def set_main_code(&code) @main_code = code end def set_last_code(&code) @last_code = code end def run_fancy_loop @boring_array.each_with_index do |item, i| case i when 0 then @first_code.call(item) when @boring_array.size - 1 then @last_code.call(item) else @main_code.call(item) end end end end fancy_array = FancyArray.new(["Matti Nykanen", "Erik Johnsen", "Michael Edwards"]) fancy_array.set_first_code {|item| puts "#{item} came first in ski jumping at the 1988 Winter Olympics"} fancy_array.set_main_code {|item| puts "#{item} did not come first or last in ski jumping at the 1988 Winter Olympics"} fancy_array.set_last_code {|item| puts "#{item} came last in ski jumping at the 1988 Winter Olympics"} fancy_array.run_fancy_loop

produce

Matti Nykanen came first in ski jumping at the 1988 Winter Olympics Erik Johnsen did not come first or last in ski jumping at the 1988 Winter Olympics Michael Edwards came last in ski jumping at the 1988 Winter Olympics

Editar : la answer de Svante (con la sugerencia de molf) a una pregunta relacionada muestra cómo pasar múltiples bloques de código a un único método:

class FancierArray < Array def each_with_first_last(first_code, main_code, last_code) each_with_index do |item, i| case i when 0 then first_code.call(item) when size - 1 then last_code.call(item) else main_code.call(item) end end end end fancier_array = FancierArray.new(["Matti Nykanen", "Erik Johnsen", "Michael Edwards"]) fancier_array.each_with_first_last( lambda {|person| puts "#{person} came first in ski jumping at the 1988 Winter Olympics"}, lambda {|person| puts "#{person} did not come first or last in ski jumping at the 1988 Winter Olympics"}, lambda {|person| puts "#{person} came last in ski jumping at the 1988 Winter Olympics"})


Si el código para la primera y última iteración no tiene nada en común con el código para las otras iteraciones, también podría hacer:

do_something( a.first ) a[1..-2].each do |x| do_something_else( x ) end do_something_else_else( a.last )

Si los diferentes casos tienen algún código en común, tu camino está bien.


Si está dispuesto a agregar un texto estándar, puede agregar algo como esto a la clase de matriz:

class Array def each_fl each_with_index do |x,i| yield [i==0 ? :first : (i==length-1 ? :last : :inner), x] end end end

y luego en cualquier lugar que necesites, obtienes la siguiente sintaxis:

[1,2,3,4].each_fl do |t,x| case t when :first puts "first: #{x}" when :last puts "last: #{x}" else puts "otherwise: #{x}" end end

para el siguiente resultado:

first: 1 otherwise: 2 otherwise: 3 last: 4


Si no te importa que la "última" acción ocurra antes del material en el medio, entonces este parche de mono:

class Array def for_first return self if empty? yield(first) self[1..-1] end def for_last return self if empty? yield(last) self[0...-1] end end

Permite esto:

%w(a b c d).for_first do |e| p [''first'', e] end.for_last do |e| p [''last'', e] end.each do |e| p [''middle'', e] end # => ["first", "a"] # => ["last", "d"] # => ["middle", "b"] # => ["middle", "c"]


Si sabe que los elementos del conjunto son únicos (a diferencia de este caso), puede hacer esto:

a = [1,2,3,4,5] a.each_with_index do |x, i| if x == a.first print x+1 elsif x == a.last print x*10 else print x end end


Veo muchos hacks aquí que están bastante cerca, pero todos dependen en gran medida de que el iterador dado tenga un tamaño fijo y NO sea un iterador. También me gustaría proponer guardar el elemento anterior a medida que avanzas para conocer el primer / último elemento que se repitió.

previous = {} elements.each do |element| unless previous.has_key?(:element) # will only execute the first time end # normal each block here previous[:element] = element end # the last element will be stored in previous[:element]