我的应用程序需要处理固定大小的阵列。问题是,有时元素是零,但零是禁止的价值。我认为一个简单的方法是用替换零值与最接近的非零值(就在之前或之后)。完成红宝石阵列
零值可以是第一个,最后一个或者甚至是多个。这里有一些我正在寻找的例子:
[1,2,3,nil,5] => [1,2,3,3,5]
[nil,2,3,4,5] => [2,2,3,4,5]
[1,nil,nil,4,5] => [1,1,4,4,5]
我相信有一个优雅的方式来做到这一点。你能帮我吗?
我的应用程序需要处理固定大小的阵列。问题是,有时元素是零,但零是禁止的价值。我认为一个简单的方法是用替换零值与最接近的非零值(就在之前或之后)。完成红宝石阵列
零值可以是第一个,最后一个或者甚至是多个。这里有一些我正在寻找的例子:
[1,2,3,nil,5] => [1,2,3,3,5]
[nil,2,3,4,5] => [2,2,3,4,5]
[1,nil,nil,4,5] => [1,1,4,4,5]
我相信有一个优雅的方式来做到这一点。你能帮我吗?
我的第一个想法是这样的事情,现在固定的零的任意序列的一般情况...
t = nil
p = lambda do |e|
if e.nil?
e,t = t,e
else
t = e
end
e
end
r = a
while r.any? && (r.include? nil)
t = nil; r = r.map(&p)
t = nil; r = r.reverse.map(&p).reverse
end
但我挺喜欢这更好。 (API是arrayObj.merge_all
)
module Enumerable
def merge_nil
t = nil
map do |e|
if e.nil?
e,t = t,e
e
else
t = e
end
end
end
end
class Array
def merge_all
return self unless any?
t = self
t = t.merge_nil.reverse.merge_nil.reverse while t.include? nil
t
end
end
+1漂亮,优雅。 – 2009-12-03 21:13:32
虽然这似乎不适用于所有情况... – dustmachine 2009-12-03 21:45:04
正确的,取决于输入可能看起来像它可能需要一个环绕两条地图线 – DigitalRoss 2009-12-04 02:14:10
首先,将各元件配对与下一个和前一个元素
triples = array.zip([nil]+array.take(array.length-1), array.drop(1))
然后映射三元组的阵列上,像这样:
triples.map {|triple|
if triple[0].nil? then
if !triple[1].nil? then triple[1] else triple[2] end
else
triple[0]
end
}
如果有超过2个尼尔斯连续,这是行不通的,所以把它放在一个循环中并不停地调用它,直到数组中没有更多的nils。
EDIT(约尔格W¯¯米塔格):你可以让这个更简洁,可读性用解构绑定和保护条款:
ary.zip([nil] + ary.take(ary.length-1), ary.drop(1)).map {|prv, cur, nxt|
next prv unless prv.nil?
next cur unless cur.nil?
nxt
}
如果这种方式重构它,就很容易看到,所有的块正在做的是寻找在先前的故障电流下三重第一非nil
元件,其可以是更简洁地表示这样的:
ary.zip([nil] + ary.take(ary.length-1), ary.drop(1)).map {|triple|
triple.find {|el| !el.nil? }
}
这反过来,可以进一步通过使用Array#compact
简化。
你真的不提你用什么为阵,但也许被替换为0零将更有意义,因为如果你想利用平均值也不会影响结果或...
[1,2,3,nil,5].map { |el| el ? el : 0 }
我和你在一起。这是简单的方法。更好的是: [1,2,3,nil,5] .map {| n | n || 0} – 2009-12-04 04:42:21
你绝对没错Ben,你的版本更加清洁:-) – 2009-12-06 23:26:07
这是我的解决方案。它将适用于数组中的任意数量的nil
,并且如果数组中的每个元素都是nil
,则会优雅地失败。如果数组中的nil
之前有非零和非零,它会在之前或之后随机选取。
init和安全检查:
arr = [1,nil,nil,4,5]
if arr.nitems == 0
raise "all nil! don't know what to do!"
else
解决方案的肉:
while (arr.index(nil))
arr.each_index do |i|
arr[i] = [arr[i-1], arr[i+1]] [rand 2] if arr[i].nil?
end
end
的总结:
end
arr #print result for review
这已经与每个实例的测试实例(开始时为零,结束时为零,中间为双重零),并应适用于任何数组大小。
注意事项:
这是DigitalRoss解决方案直接复制,但处理的更边缘的情况下,比连续两个零。我敢肯定,DigitalRoss将能够更优雅做到这一点,并没有非idomatic红宝石while循环,但这个工程的所有测试案例
def un_nil(arr)
return arr if arr.compact.size == 0 || ! arr.include?(nil)
while arr.include?(nil)
t = nil
p = lambda do |e|
if e.nil?
e,t = t,e
else
t = e
end
e
end
t = nil; r = arr.map(&p)
t = nil; r = r.reverse.map(&p).reverse
arr = r
end
arr
end
tests = [
[1,2,3,4,5],
[1,2,3,nil,5],
[nil,2,3,4,5],
[1,nil,nil,4,5],
[1,nil,nil,nil,5],
[nil,nil,3,nil,nil],
[nil,nil,nil,nil,nil]
]
tests.each {|a| puts "Array #{a.inspect} became #{un_nil(a).inspect}" }
这将产生以下输出
Array [1, 2, 3, 4, 5] became [1, 2, 3, 4, 5]
Array [1, 2, 3, nil, 5] became [1, 2, 3, 3, 5]
Array [nil, 2, 3, 4, 5] became [2, 2, 3, 4, 5]
Array [1, nil, nil, 4, 5] became [1, 1, 4, 4, 5]
Array [1, nil, nil, nil, 5] became [1, 1, 1, 5, 5]
Array [nil, nil, 3, nil, nil] became [3, 3, 3, 3, 3]
Array [nil, nil, nil, nil, nil] became [nil, nil, nil, nil, nil]
无论如何它都会绕过while循环,所以根本不需要测试nil值的guard子句的部分并不真正需要, – 2009-12-03 21:15:56
这一切都取决于你以后想用数据做什么。它可以让你的感觉摆在平均值,但如果你有比较小的阵列和下降一点点乐趣,你可以去所有贝叶斯的东西,如下列:
require 'classifier'
$c = Classifier::Bayes.new
perm = [1, 2, 3, 4, 5].permutation(5)
perm.each { |v| $c.add_category v * "," }
perm.each { |v| $c.train v*"," , v*"," }
def guess(arr)
s = $c.classify(arr*",")
a = s.split(',').map{|s| s.to_i}
end
tests = [
[1,2,3,4,5],
[1,2,3,nil,5],
[nil,2,3,4,5],
[1,nil,nil,4,5],
[1,nil,nil,nil,5],
[nil,nil,3,nil,nil],
[nil,nil,nil,nil,nil]
]
tests.each { |t| puts "Array #{t.inspect} became #{guess(t).inspect}" }
输出如下所示:
Array [1, 2, 3, 4, 5] became [1, 2, 3, 4, 5]
Array [1, 2, 3, nil, 5] became [1, 2, 3, 4, 5]
Array [nil, 2, 3, 4, 5] became [1, 2, 3, 4, 5]
Array [1, nil, nil, 4, 5] became [1, 2, 3, 4, 5]
Array [1, nil, nil, nil, 5] became [1, 2, 3, 4, 5]
Array [nil, nil, 3, nil, nil] became [1, 2, 3, 4, 5]
Array [nil, nil, nil, nil, nil] became [1, 2, 3, 4, 5]
这是@Callum's solution变体:
require 'test/unit'
class TestArrayCompletion < Test::Unit::TestCase
def test_that_the_array_gets_completed_correctly
ary = [nil,1,2,nil,nil,3,4,nil,nil,nil,5,6,nil]
expected = [1,1,2,2,3,3,4,4,nil,5,5,6,6]
actual = ary.zip([nil]+ary.take(ary.length-1), ary.drop(1)).
map(&:compact).map(&:first)
assert_equal expected, actual
end
end
这让我感到这将是那么令人惊讶传播的最后一个非零值,而不是展望一个非零值:
def fill_in_array(ary)
last_known = ary.find {|elem| elem} # find first non-nil
ary.inject([]) do |new, elem|
if elem.nil?
new << last_known
else
new << elem
last_known = elem
end
new
end
end
p fill_in_array [1,2,3,nil,5] # => [1,2,3,4,5]
p fill_in_array [1,nil,nil,4,5] # => [1,1,1,4,5]
p fill_in_array [nil,nil,nil,4,5] # => [4,4,4,4,5]
“零值可以是第一,最后甚至是倍数”。你能解释一下吗?在你的第三个例子中,数组中间怎么能有一个零? – 2009-12-03 20:02:17
连续可以有多于2个零值吗? – DigitalRoss 2009-12-03 20:35:11
是的......应该至少有一个非零值。 – 2009-12-04 07:55:39