Ruby on rails Ruby/Rails中循环中的第一个和最后一个指示器?
Ruby/Rails做了很多很酷的事情,当涉及到基本的糖的时候,我想有一个非常常见的场景,我想知道是否有人做过助手或类似的事情Ruby on rails Ruby/Rails中循环中的第一个和最后一个指示器?,ruby-on-rails,ruby,syntactic-sugar,Ruby On Rails,Ruby,Syntactic Sugar,Ruby/Rails做了很多很酷的事情,当涉及到基本的糖的时候,我想有一个非常常见的场景,我想知道是否有人做过助手或类似的事情 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 恕我直言,这是
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
恕我直言,这是你想要的。。。是否有一种ruby方法来处理循环的第一个和最后一个
[编辑]我认为理想情况下,这将是对带有参数的数组的扩展(数组实例、所有元素函数、第一个元素函数、最后一个元素函数)。。。但是我愿意接受其他想法。如果第一次和最后一次迭代的代码与其他迭代的代码没有任何共同之处,您也可以这样做:
do_something( a.first )
a[1..-2].each do |x|
do_something_else( x )
end
do_something_else_else( a.last )
如果不同的情况有一些相同的代码,那么您的方式就可以了。Ruby中没有“第一次(最后一次)执行此操作”语法。但如果你想要简洁,你可以这样做:
a.each_with_index do |x, i|
print (i > 0 ? (i == a.length - 1 ? x*10 : x) : x+1)
end
结果是您所期望的:
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
有意思的问题,我也想过一点 我认为您必须创建三个不同的block/proc/无论调用什么,然后创建一个调用正确block/proc/无论什么的方法。(很抱歉说得含糊不清-我还不是黑带元程序员)[编辑:不过,我是从底层的人那里抄袭的) 产生
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
编辑:Svante(根据molf的建议)对相关问题的回答显示了如何将多个代码块传递给单个方法:
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"})
class FancierArray
如果愿意,您可以获取第一个和最后一个元素,并以不同的方式处理它们
first = array.shift
last = array.pop
process_first_one
array.each { |x| process_middle_bits }
process_last_one
如果您愿意添加一些样板文件,可以向array类中添加如下内容:
class Array
def each_fl
each_with_index do |x,i|
yield [i==0 ? :first : (i==length-1 ? :last : :inner), x]
end
end
end
然后,您可以在需要的任何地方使用以下语法:
[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
对于以下输出:
first: 1
otherwise: 2
otherwise: 3
last: 4
如果知道数组中的项是唯一的(与本例不同),可以执行以下操作:
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
将数组划分为多个区域,每个区域内的元素的行为应该不同。将由此创建的每个区域映射到一个块
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
可以手动创建范围,但下面的帮助程序使其更容易:
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
用法:
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
如果你能做到呢
%w(a b c d).each.with_position do |e, position|
p [e, position] # => ["a", :first]
# => ["b", :middle]
# => ["c", :middle]
# => ["d", :last]
end
还是这个
%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
在MRI>=1.8.7中,只需要这个猴子贴片:
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
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
它有一个小的状态引擎,因为它必须向前看一次迭代
诀窍是,每个,每个带有索引的枚举,如果没有给定块,则返回一个枚举数。枚举数完成枚举数所做的一切,并且做得更多。但对我们来说,重要的是,我们可以使用monkey patch Enumerator添加另一种迭代方式,“包装”现有的迭代,不管它是什么。如果您不介意“最后一次”的话动作发生在中间的东西之前,然后这个猴子补丁:
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
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
允许这样做:
%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"]
或者一种小小的领域特定语言:
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"]
以及使其运行的代码:
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
我开始想,“如果你能将多个块传递给一个Ruby函数,那么你就可以有一个巧妙而简单的解决方案来解决这个问题。”然后我意识到DSL的小把戏就像传递多个块一样。有时候for循环就是你最好的选择
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
我知道这个问题已经得到了回答,但这种方法没有副作用,也不会检查第13、14、15、10、1000、1000……条记录是第一条记录,还是最后一条记录
以前的答案在任何数据结构类中都会失败。我不时需要这个功能,因此为此我专门设计了一个小类 最新版本载于: 样本:
("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
实际等级:
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
模块可枚举
#您的每个_与_位置方法
定义每个位置和块
每个带位置。每个(自身和块)
结束
结束
类的每个位置
属性读取器:索引
类KISS
我忍不住说:)这并不是为了性能而调整的,虽然我想它应该不会比这里的大多数其他答案慢很多。这都是关于糖的
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
例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
例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
正如许多人指出的,每个带有索引的\u似乎都是关键。我有我喜欢的代码块
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
或
或者通过数组扩展
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
我在这里看到了很多非常接近的攻击,但都严重依赖于给定的迭代器,它具有固定的大小,而不是迭代器。我还建议在迭代时保存上一个元素,以了解迭代的第一个/最后一个元素
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]
很酷的主意…事实上,即使有共同点,你也可以先做a.each,然后在最后做a.first和a.last来附加/覆盖你所做的。a.first==a[0];a.last==a[-1],顺便说一句,应该是答案…即使他们在comon中有东西,你也不会问数组中的每个元素是第一个还是最后一个,这让我笑了。给试图解决这个问题的人一个评论-一个真正的Ruby解决方案应该处理的不仅仅是每个带有索引的元素,还有映射
,注入代码>,等等。对于有一个元素的数组,该元素是列表中的第一个,还是列表中的最后一个,还是两者都有?这里有很多很好的评论-我将暂时不讨论这个问题,希望我们能获得更多的投票,以获得最佳解决方案。我认为
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]