连续字母频率

4
我将尝试编写代码来确定字符串中连续字母的频率。
例如:
"aabbcbb" => ["a",2],["b",2],["c", 1], ["b", 2]

我的代码能够给出第一个字母的频率,但无法继续计算下一个字母。

def encrypt(str)
  array = []
  count = 0
   str.each_char do |letter|

    if array.empty?
      array << letter
      count += 1
    elsif array.last == letter
      count += 1
    else
      return [array, count]
      array = []
    end
  end
end

2
你的代码有语法错误:undefined local variable or method 'array'。在 def 后面只是缺少了 array = [],还是你的示例中还有其他缺失的代码? - knut
1
吹毛求疵,但那不是一个语法错误。 - user229044
抱歉,忘记包括初始数组=[]。 - Jgoo83
7个回答

11
p "aabbcbb".chars.chunk{|c| c}.map{|c, a| [c, a.size]} 
# => [["a", 2], ["b", 2], ["c", 1], ["b", 2]]

哦,不错。我之前不知道有“chunk”这个函数。 - Ajedi32
我不太明白chunk的工作原理。我在ruby-doc.org网站上尝试阅读相关内容,但在这种情况下它是如何工作的仍然让我有些困惑。抱歉打扰了。 - Jgoo83
1
在 Ruby 2.2 中,您可以使用 &:itself - sawa
1
@Kozby Enumerable#chunk 的作用是根据块的返回值将数组分成连续的块。例如,"aabbcbb".chars.chunk{|c| c}.to_a 返回 [["a", ["a", "a"]], ["b", ["b", "b"]], ["c", ["c"]], ["b", ["b", "b"]]]。如果您仍然感到困惑,请提出另一个问题,解释您对 RubyDoc 文档的不理解之处,我或其他人会更详细地解释。 - Ajedi32
@Ajedi32 谢谢!我仔细审查了一下,现在明白它是如何工作的。我是自学的,所以有些东西比其他东西更容易理解。我已经想出了我想要的内容,如下所述,虽然它不是"Ruby式"的,但确实有效。 - Jgoo83
@steenslag 很抱歉在一个古老的线程上进行评论... .map{|c,a| |c, a.size]} 发生了什么?我知道结果会是什么,但是在两个竖杠之间没有提到 map 有两个对象。 - paulywill

8
"aabbcbb".chars.slice_when(&:!=).map{|a| [a.first, a.length]}
# => [["a", 2], ["b", 2], ["c", 1], ["b", 2]]

3
Enumerable#slice_when的涂料还很新。我看到它和slice_after是在2.2版本中添加的。 - Cary Swoveland

4

有一个基于反向引用的简单正则表达式解决方案:

"aabbbcbb".scan(/((.)\2*)/).map { |m,c| [c, m.length] }
# => [["a", 2], ["b", 3], ["c", 1], ["b", 2]]

但是出于清晰度(以及几乎肯定的效率),我更喜欢使用chunk方法


实际上,出于好奇心,我撰写了一个快速基准测试,scanchunk.map快四倍多,但除非你实际需要数十万次这样做,否则我仍然会使用chunk.map,因为它更清晰易懂:

require 'benchmark'

N = 10000

data = ('a'..'z').map { |c| c * 10 }.join("")

Benchmark.bm do |bm|
  bm.report do
    N.times { data.chars.chunk{ |c| c }.map { |c, a| [c, a.size] } }
  end

  bm.report do
    N.times { data.scan(/((.)\2*)/).map { |m,c| [c, m.size] } }
  end
end
     user     system      total        real
 0.800000   0.010000   0.810000 (  0.803824)
 0.190000   0.000000   0.190000 (  0.192915)

谢谢你的基准测试。我早些时候看到了你的答案,对效率比较感到好奇。在看到一些非常快速的正则表达式之后,我怀疑你的解决方案可能会表现得不错,但是我对胜利的幅度感到惊讶。我也认为你的解决方案阅读起来很好。 - Cary Swoveland

0
你需要建立一个结果数组,而不仅仅停留在第一个结果上:
def consecutive_frequencies(str)
  str.each_char.reduce([]) do |frequencies_arr, char|
    if frequencies_arr.last && frequencies_arr.last[0] == char
      frequencies_arr.last[1] += 1
    else
      frequencies_arr << [char, 1]
    end

    frequencies_arr
  end
end

0

@steenslag 给出了我本来会给出的答案,所以我尝试一些不同的东西。

"aabbcbb".each_char.with_object([]) { |c,a| (a.any? && c == a.last.first) ?
  a.last[-1] += 1 : a << [c, 1] }
  #=> [["a", 2], ["b", 2], ["c", 1], ["b", 2]]

0
def encrypt(str)

  count = 0
  array = []
  str.chars do |letter|

    if array.empty?
      array << letter
      count += 1
    elsif array.last == letter
      count += 1
    else
      puts "[#{array}, #{count}]"
      array.clear
      count = 0
      array << letter
      count += 1
    end
  end
  puts "[#{array}, #{count}]"
end

-2

你的实现存在几个错误,我建议使用哈希表(而不是数组),并尝试使用以下代码:

def encrypt(str)

  count = 0
  hash = {}
  str.each_char do |letter|

    if hash.key?(letter)
      hash[letter] += 1
    else
      hash[letter] = 1
    end

  end

  return hash
end

puts encrypt("aabbcbb")

1
这只是计算字符数吧?这不是 OP 想要的。 - Ajedi32
1
也非常不像Ruby风格。放弃if/else,直接给你的哈希表设定默认值:hash = Hash.new(0) - user229044
问题要求我使用数组,而不是哈希表。我确实忘记包含新的数组,现在已经进行了编辑。顺便说一下,我是一个非常初级的学习者。 - Jgoo83
哦,我明白了。我没有意识到你想要连续实例的计数。我的方法就不适用了。 - Hector Correa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接