aro*_*ick 55 ruby ruby-on-rails 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
Run Code Online (Sandbox Code Playgroud)
原谅丑陋,但这可以达到人们想要的......是否有一种红宝石的方式来对循环的第一个和最后一个做一些事情?
[编辑]我认为理想情况下这将是带有参数的数组的扩展(数组实例,所有元素函数,第一个元素函数,最后元素函数)......但我对其他想法持开放态度.
Mat*_*chu 31
如果您愿意,可以抓取第一个和最后一个元素并以不同方式处理它们.
first = array.shift
last = array.pop
process_first_one
array.each { |x| process_middle_bits }
process_last_one
Run Code Online (Sandbox Code Playgroud)
sep*_*p2k 14
如果第一次和最后一次迭代的代码与其他迭代的代码没有任何共同之处,您还可以执行以下操作:
do_something( a.first )
a[1..-2].each do |x|
do_something_else( x )
end
do_something_else_else( a.last )
Run Code Online (Sandbox Code Playgroud)
如果不同的情况有一些共同的代码,你的方式很好.
如果你能这样做怎么办?
%w(a b c d).each.with_position do |e, position|
p [e, position] # => ["a", :first]
# => ["b", :middle]
# => ["c", :middle]
# => ["d", :last]
end
Run Code Online (Sandbox Code Playgroud)
或这个?
%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
Run Code Online (Sandbox Code Playgroud)
在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
Run Code Online (Sandbox Code Playgroud)
它有一个小的状态引擎,因为它必须向前看一次迭代.
诀窍是each,each_with_index,&c.如果没有阻止,则返回枚举器.枚举器可以完成Enumerable所做的所有事情.但对我们来说,重要的是我们可以通过猴子修补Enumerator来添加一种迭代方式,"包装"现有的迭代,无论它是什么.
或者一点点领域特定语言:
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"]
Run Code Online (Sandbox Code Playgroud)
和它的代码:
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
Run Code Online (Sandbox Code Playgroud)
我开始思考,"如果只有你可以将多个块传递给Ruby函数,那么你可以对这个问题有一个灵活而简单的解决方案." 然后我意识到DSL的玩法几乎就像传递多个块一样.
正如许多人所指出的,each_with_index似乎是关键.我有这个我喜欢的代码块.
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
Run Code Online (Sandbox Code Playgroud)
要么
array.each_with_index do |item,index|
if index == 0
# first item
end
# all items
if index == array.length-1
# last item
end
end
Run Code Online (Sandbox Code Playgroud)
或者通过数组扩展
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
Run Code Online (Sandbox Code Playgroud)
如果您愿意添加一些样板文件,可以在数组类中添加以下内容:
class Array
def each_fl
each_with_index do |x,i|
yield [i==0 ? :first : (i==length-1 ? :last : :inner), x]
end
end
end
Run Code Online (Sandbox Code Playgroud)
然后你需要的任何地方,你得到以下语法:
[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
Run Code Online (Sandbox Code Playgroud)
对于以下输出:
first: 1
otherwise: 2
otherwise: 3
last: 4
Run Code Online (Sandbox Code Playgroud)