Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/58.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails Ruby/Rails中循环中的第一个和最后一个指示器?_Ruby On Rails_Ruby_Syntactic Sugar - Fatal编程技术网

Ruby on 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 恕我直言,这是

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
恕我直言,这是你想要的。。。是否有一种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]