我正在处理一个问题,我想要比较两个相等长度的字符串,char by char。对于字符不同的每个索引,我需要将计数器递增1.现在我有这个:
def compute(strand1, strand2)
raise ArgumentError, "Sequences are different lengths" unless strand1.length == strand2.length
mutations = 0
strand1.chars.each_with_index { |nucleotide, index| mutations += 1 if nucleotide != strand2[index] }
mutations
end
end
我是这种语言的新手,但这对我来说非常非Ruby。是否有一个单行程可以巩固设置计数器,递增计数器然后返回计数器的过程?
我正在考虑选择所有不匹配的字符然后获得结果数组的大小。但是,据我所知,没有select_with_index
方法。我也在研究inject
,但似乎无法弄清楚如何在这种情况下应用它。
答案 0 :(得分:5)
可能最简单的答案就是计算差异:
strand1.chars.zip(strand2.chars).count{|a, b| a != b}
答案 1 :(得分:2)
以下是一些单行:
strand1.size.times.select { |index| strand1[index] != strand2[index] }.size
这不是最好的,因为它会生成一个大小为O(n)的中间数组。
strand1.size.times.inject(0) { |sum, index| sum += 1 if strand1[index] != strand2[index]; sum }
不占用额外的内存,但有点难以阅读。
strand1.chars.each_with_index.count { |x, index| x != strand2[index] }
我顺其自然。感谢user12341234提及count
,这是建立在此基础上的。
<强>更新强>
我的机器上的基准测试与@CarySwoveland获得的结果不同:
user system total real
mikej 4.080000 0.320000 4.400000 ( 4.408245)
user12341234 3.790000 0.210000 4.000000 ( 4.003349)
Nik 2.830000 0.170000 3.000000 ( 3.008533)
user system total real
mikej 3.590000 0.020000 3.610000 ( 3.618018)
user12341234 4.040000 0.140000 4.180000 ( 4.183357)
lazy user 4.250000 0.240000 4.490000 ( 4.497161)
Nik 2.790000 0.010000 2.800000 ( 2.808378)
这并不是要指出我的代码具有更好的性能,而是提到环境在选择任何特定的实现方法中起着重要作用。
我在具有足够内存的12-Core i7-3930K上使用本机3.13.0-24-generic#47-Ubuntu SMP x64运行此功能。
答案 2 :(得分:2)
这是一个刚刚延伸的评论,所以请不要赞成(downvotes还可以)。
先生们:启动引擎!
测试问题
def random_string(n, selection)
n.times.each_with_object('') { |_,s| s << selection.sample }
end
n = 5_000_000
a = ('a'..'e').to_a
s1 = random_string(n,a)
s2 = random_string(n,a)
基准代码
require 'benchmark'
Benchmark.bm(12) do |x|
x.report('mikej') do
s1.chars.each_with_index.inject(0) {|c,(n,i)| n==s2[i] ? c : c+1}
end
x.report('user12341234') do
s1.chars.zip(s2.chars).count{|a,b| a != b }
end
x.report('lazy user') do
s1.chars.zip(s2.chars).lazy.count{|a,b| a != b }
end
x.report('Nik') do
s1.chars.each_with_index.count { |x,i| x != s2[i] }
end
end
<强>结果
mikej 6.220000 0.430000 6.650000 ( 6.651575)
user12341234 6.600000 0.900000 7.500000 ( 7.504499)
lazy user 7.460000 7.800000 15.260000 ( 15.255265)
Nik 6.140000 3.080000 9.220000 ( 9.225023)
user system total real
mikej 6.190000 0.470000 6.660000 ( 6.662569)
user12341234 6.720000 0.500000 7.220000 ( 7.223716)
lazy user 7.250000 7.110000 14.360000 ( 14.356845)
Nik 5.690000 0.920000 6.610000 ( 6.621889)
[编辑:我添加了'{user}个版本''user12341234'。与lazy
版本的枚举器一样,在使用的内存量和执行时间之间存在权衡。我做了好几次。在这里,我报告两个典型的结果。 'mikej'和'user12341234'的变化很小,lazy
的变化较大,lazy user
的变化较大。]
答案 3 :(得分:1)
inject
确实可以用一个单行程来做到这一点,所以你走在正确的轨道上:
strand1.chars.each_with_index.inject(0) { |count, (nucleotide, index)|
nucleotide == strand2[index] ? count : count + 1 }
我们从初始值0开始,然后如果2个字母相同,我们只返回累加器值的当前值,如果字母不同,我们加1。
此外,请注意,each_with_index
在没有阻止的情况下被调用。当像这样调用时,它返回一个枚举器对象。这允许我们使用其他枚举器方法之一(在本例中为inject
)与each_with_index
返回的值和索引对,允许each_with_index
和inject
的功能要合并。
编辑:看着它,user12341234's解决方案是一种很好的方式。善用zip
!我可能会这样做。