假设我有一个这样的字符串数组:
array = ["foo", "(bar)", "baaz", "quux", "herp", "(derp)"]
我需要将以"("开头的项与前一项连接起来,以获得如下输出:
["foo (bar)", "baaz", "quux", "herp (derp)"]
我猜它必须是类似于获得与/^(/
匹配的数组项的索引,然后在块中迭代原始数组,在index-1..index
处连接项,并在index
处删除
array.slice_before{|s| !s.start_with?("(")}.map{|a| a.join(" ")}
# => ["foo (bar)", "baaz", "quux", "herp (derp)"]
从另一个方向看——连接整个字符串,然后沿着后面没有(
的空格拆分它:
array.join(' ').split(/ (?!()/)
# => ["foo (bar)", "baaz", "quux", "herp (derp)"]
我无法改进@sawa的答案,但我可以提供另一种方法,一些读者可能会在其他时间和其他地方发现有用的方法:
array = ["foo", "(bar)", "baaz", "quux", "herp", "(derp)"]
arr = []
enum = array.each
loop do
arr << enum.next
next_up = enum.peek
if next_up[0] == ?(
arr[-1] += (" " + next_up)
enum.next
end
end
arr #=> ["foo (bar)", "baaz", "quux", "herp (derp)"]
事情就是这样。
arr = []
enum = array.each
#=> #<Enumerator: ["foo", "(bar)", "baaz", "quux", "herp", "(derp)"]:each>
现在让我们遍历循环,直到引发StopIteration
异常:
s = enum.next #=> "foo"
arr << s #=> ["foo"]
next_up = enum.peek #=> "(bar)"
next_up[0] == ?( #=> true
arr[-1] += (" " + next_up) #=> "foo (bar)"
arr #=> ["foo (bar)"]
enum.next #=> "(bar)" (discard)
s = enum.next #=> "baaz"
arr << s #=> ["foo (bar)", "baaz"]
next_up = enum.peek #=> "quux"
next_up[0] == ?( #=> false
s = enum.next #=> "quux"
arr << s #=> ["foo (bar)", "baaz", "quux"]
next_up = enum.peek #=> "herp"
next_up[0] == ?( #=> false
s = enum.next #=> "herp"
arr << s #=> ["foo (bar)", "baaz", "quux", "herp"]
next_up = enum.peek #=> "(derp)"
next_up[0] == ?( #=> true
arr[-1] += (" " + next_up) #=> "herp (derp)"
arr #=> ["foo (bar)", "baaz", "quux", "herp (derp)"]
enum.next #=> "(derp)" (discard)
s = enum.next #=> StopIteration: iteration reached an end
StopIteration
异常由Kernel#loop通过打破循环来处理。
arr #=> ["foo (bar)", "baaz", "quux", "herp (derp)"]
这里有另一种方法,使用Enumerable#chunk。我假设数组的第一个元素的第一个字符不是(
,但是如果这个假设不正确,当然可以修改方法。
def doit(array)
array.chunk { |s| s[0] == ?( }
.map(&:last)
.each_slice(2)
.map { |arr| (arr.size == 2) ? [arr.first[0..-2],
[arr.first.last, *arr.last].join(' ')] : arr }
.flatten
end
array = ["foo", "(bar)", "baaz", "quux", "herp", "(derp)"]
doit(array) #=> ["foo (bar)", "baaz", "quux", "herp (derp)"]
array = ["foo", "(bar)", "(anther bar)", "quux"]
doit(array) #=> ["foo (bar) (anther bar)", "quux"]
array = ["foo", "(bar)", "baaz", "quux", "herp", "(derp)"]
enum1 = array.chunk { |s| s[0] == ?( }
#=> #<Enumerator: #<Enumerator::Generator:0x00000101142ce0>:each>
enum1.to_a # elements to be enumerated (for information only)
#=> [[false, ["foo"]], [true, ["(bar)"]],
# [false, ["baaz", "quux", "herp"]], [true, ["(derp)"]]]
a = enum1.map(&:last)
#=> [["foo"], ["(bar)"], ["baaz", "quux", "herp"], ["(derp)"]]
enum2 = a.each_slice(2)
#=> #<Enumerator: [["foo"], ["(bar)"], ["baaz", "quux", "herp"],
# ["(derp)"]]:each_slice(2)>
enum2.to_a # elements to be enumerated (for information only)
#=> [[["foo"], ["(bar)"]], [["baaz", "quux", "herp"], ["(derp)"]]]
c = enum2.map { |arr| (arr.size==2) ? [arr.first[0..-2],
[arr.first.last, *arr.last].join(' ')] : arr }
#=> [[[], "foo (bar)"], [["baaz", "quux"], "herp (derp)"]]
c.flatten
#=> ["foo (bar)", "baaz", "quux", "herp (derp)"]