在Ruby中比较两个包含字符串字符串的数组

时间:2014-11-19 21:40:42

标签: ruby arrays anagram

如果我的代码关闭,请原谅我。我仍然掌握在Ruby on Rails中,这似乎有微妙的差异,因为我学到更多“只是Ruby”,尽管公平地说我不确定我的代码是否会以Ruby on Rails格式通过。我离题了。

我试图比较两个包含一组字符串的数组。我想做几件事。 1)确保数组的单词数相同,否则练习没有实际意义。 2)比较数组中的第一个字与第二个数组中的第一个字。换句话说,我从不想比较数组“a”中的单词1和数组“b”中的单词4。我正在努力找到一个解决方案,重新排序任何给定单词中的字符,将其与第二个数组中相应单词中的重新排序字符进行比较,如果它是一个字谜,则打印1(一旦排序,这个想法就是两个单词如果它们不相符,则为0或0;

在下面的示例中,我想要打印的内容是:

0
0
1
1

......但事实并非如此。思考?我担心这与局部变量问题有关,但我不确定。

    a = ['hello', 'goodbye', 'pants', 'baa']
    b = ['helio', 'godbye', 'spant', 'aba']

    x = a.length
    y = b.length
    z = 0

    x = y? do
        while z < x do
            if a.find(z).chars.sort.join == b.find(z).chars.sort.join
                puts 1
            else
                puts 0
            end

            z += 1
        end
    end

4 个答案:

答案 0 :(得分:3)

这不一定是火箭科学。事实上,只要你能够始终如一地代表每个阵列,它就是一个明智的选择:

a = ['hello', 'goodbye', 'pants', 'baa']
b = ['helio', 'godbye', 'spant', 'aba']
c = ['lohel', 'goedboy', 'spant', 'aab']

def anagram_flatten(array)
  array.collect do |word|
    word.chars.sort.join
  end
end

puts anagram_flatten(a) == anagram_flatten(b)
# => false
puts anagram_flatten(a) == anagram_flatten(c)
# => true

当一个简单的数组与阵列比较无论如何都非常快时,我不担心部分比较。

答案 1 :(得分:3)

[编辑我编辑了我的答案,将@raph建议的效率改进纳入了对该问题的评论(下面的方法anagram? )。这可能没有必要,但我认为这是一个好主意,它应该得到一些曝光。我也给出了一个详细的解释,因为OP是Ruby的新手,也可能是其他读者。]

您可以考虑按以下方式执行此操作。

<强> 代码

def anagrams(a, b)
  return nil unless a.size == b.size
  a.zip(b).map { |aw,bw| anagram?(aw,bw) ? 1 : 0 }
end

def anagram?(aw, bw)
  return false unless aw.size == bw.size
  counts = aw.downcase.each_char.with_object(Hash.new(0)) { |c,h| h[c] += 1 }
  bw.downcase.each_char do |c|
    return false unless counts[c] > 0
    counts[c] -= 1
  end
  true
end

<强> 实施例

a = ['hello', 'goodbye', 'pants', 'baa']
b = ['helio', 'godbye', 'Spant', 'aba']
anagrams(a, b)
  #=> [0, 0, 1, 1]

<强> 解释

anagrams方法

对于上面的例子,

a.size #=> 4
b.size #=> 4

所以我们不会在nil的第一行返回anagrams

接下来,

c = a.zip(b)
  #=> [["hello", "helio"], ["goodbye", "godbye"],
  #    ["pants", "Spant"], ["baa", "aba"]]

假设anagram?暂时按预期工作:

c.map { |e| anagram?(e.first, e.last) ? 1 : 0 }
  #=> [0, 0, 1, 1]

Enumerable#mapc(一个双元素数组)的每个元素传递给块。 1 。然而,更清楚的是分解(或“消除歧义”)那些数组并将它们包含的两个单词中的每一个分配给块变量 2

c.map { |aw,bw| anagram?(aw,bw) ? 1 : 0 }
  #=> [0, 0, 1, 1]

传入的第一个元素是["hello", "helio"],所以

aw => "hello"
bw #=> "helio"

我们执行

anagram?("hello", "helio") ? 1 : 0
  #=> 0

这是

的简写
if anagram?("hello", "helio")
  1
else
  0
end
  #=> 0

anagram?方法

现在让我们转到anagram?

aw = "hello"
bw = "helio"

由于

aw.size == bw.size #=> true

我们不会回来。

计算第一个单词中字母的频率

现在让我写下anagram?的下几行略有不同:

counts = Hash.new(0)
  #=> {}
aw_down = aw.downcase 
  #=> "hello"
aw_down.each_char { |c| counts[c] += 1 }
  #=> "hello"
counts
  #=> {"h"=>1, "e"=>1, "l"=>2, "o"=>1}

(最后一行只是为了显示散列的值。)

在第一行中,我们创建一个默认值为零的哈希counts。所有这些意味着,如果counts不包含密钥k,则counts[k]将返回默认值。 非常重要: 这样做不会改变哈希 3

String#each_char 4 "hello"的每个字符传递到块中,并将其分配给块变量c。最初是c='h'h={}。然后我们执行

counts['h'] += 1

这是

的简写
counts['h'] = counts['h'] + 1

由于counts还没有密钥'h',右侧的counts['h']会返回默认值:

counts['h'] = 0 + 1 #=> 1
counts #=> {"h"=>1}

同样,在'e'和第一个'l'传递给块后,我们有:

counts #=> {"h"=>1, "e"=>1, "l"=>1} 

但是,当我们传递第二个'l'时,我们执行

counts['l'] = counts['l'] + 1
  #=>    1 + 1
  #=> 2

我们完成了

counts #=> {"h"=>1, "e"=>1, "l"=>2, "o"=>1}

方法Enumerable#each_with_object将成为好朋友

此方法仅用于保存一些步骤。它允许我们写:

counts = Hash.new(0)
aw_down.each_char { |c| counts[c] += 1 }

作为

counts = aw_down.each_with_object(Hash.new(0)) { |c,h| h[c] += 1 }

我们也可以摆脱这条线

aw_down = aw.downcase 

写作

counts = aw.downcase.each_char.with_object(Hash.new(0)) { |c,h| h[c] += 1 }

这似乎是一个小小的保存,但在许多其他情况下,使用each_with_object和其他Enumerable类方法允许链接方法,这非常有用。

减少第二个字母中字母的字母数

提取

counts #=> {"h"=>1, "e"=>1, "l"=>2, "o"=>1}

我们现在执行

bw_down = bw.downcase
  #=> "helio"
"helio".each_char do |c|
  return false unless counts[c] > 0
  counts[c] -= 1
end

首先,'h'被传递到块中。作为counts['h'] #=> 1,我们执行counts['h'] -= 1,现在

counts #=> {"h"=>0, "e"=>1, "l"=>2, "o"=>1}`.

'e''l'传递到块后

counts #=> {"h"=>0, "e"=>0, "l"=>1, "o"=>1}

但是当我们通过'i'时,我们会找到

counts['i'] #=> 0

(即,返回默认值为零,我们不想将counts['i']设置为-1)所以我们返回false,得出结论是这两个词是不是字谜。 (如果第二个单词是"heeio",当第二个false传递给该区块时,我们会返回'e'。)

我们有字谜吗?

由于两个单词的长度相同,如果我们能够处理第二个单词的所有字符而不返回false,我们必须结束

counts #=> {"h"=>0, "e"=>0, "l"=>0, "o"=>0}

(无需检查!),意思是两个单词是字谜,所以在这种情况下我们会将true返回anagrams 5 因此,最后一行anagram?

备注

1 在幕后,这就是发生的事情:

enum = c.map
  #=> #<Enumerator: [["hello", "helio"], ["goodbye", "godbye"],
  #                  ["pants", "Spant"], ["baa", "aba"]]:map>

在这里,我们可以看到枚举器将传递给块的元素,但有时您需要将枚举器转换为数组以获取该信息:

enum.to_a
  #=> [["hello", "helio"], ["goodbye", "godbye"],
  #    ["pants", "Spant"], ["baa", "aba"]]

实际上,方法Array#eachenum的元素传递到块中:

enum.each { |aw,bw| anagram?(aw,bw) ? 1 : 0 }
  #=> [0, 0, 1, 1]

2 如果我们将[[1,2],3]传递给一个块,并且块变量写为|(a,b),c|,那么a=>1b=>2,{{ 1}}。这非常方便。很酷,嗯?

3

c=>3

请注意,有一种Hash#new形式采用块,它允许在引用时不添加散列中的键。

4 而不是h = Hash.new('pig') h['dog'] = 7 #=> 7 h #=> {"dog"=>7} h[0] #=> "pig" h['cat'] #=> "pig" h[{:a=>1}] #=> "pig" h #=> {"dog"=>7} 我们可以编写aw_down.each_char,但aw_down.chars.each会创建一个不必要的中间数组。 aw_down.chars,一个枚举器,只是在需要时传递值。

5 我们可以返回each_char而不是0false而不是1,在这种情况下我们可以写

true
a.zip(b).map { |aw,bw| anagram?(aw,bw) } 中的

,但让anagrams返回值为anagramstrue的数组,而不是false或更不清楚0

答案 2 :(得分:0)

关于您的代码,只需要修复两件事:

  • x = y? do应为if x == y
  • 而不是array.find(index)您需要使用array[index](具体示例:a.find(z)b.find(z)将分别成为a[z]b[z]

这是您的代码,应用了这两个修复程序。它works

a = ['hello', 'goodbye', 'pants', 'baa']
b = ['helio', 'godbye', 'spant', 'aba']

x = a.length
y = b.length
z = 0

if x == y
    while z < x do
        if a[z].chars.sort.join == b[z].chars.sort.join
            puts 1
        else
            puts 0
        end

        z += 1
    end
end

有关更多ruby-idiomatic解决方案,请参阅tadman's answer

答案 3 :(得分:0)

好的,我找到了解决方案!

for i in 0..a.length-1
  a[i].chars.sort == b[i].chars.sort ? 1 : 0
end

输出

0
0
1
1