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

如果我的代码关闭,请原谅我。 我仍然掌握在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 

[ 编辑我编辑了我的答案,将@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 

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

下一个,

 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] 

可枚举#map将c每个元素(一个双元素数组)传递给块。 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" ,当第二个'e'传递给块时,我们会返回false 。)

我们有一个字谜吗?

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

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

(不需要检查!),这意味着这两个词是字谜,所以在这种情况下我们将返回trueanagrams5因此, anagram?的最后一行anagram?

笔记

1引擎盖下,这就是发生的事情:

 enum = c.map #=> # 

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

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

它实际上是方法Array# ,它将enum元素传递给块:

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

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

3

 h = Hash.new('pig') h['dog'] = 7 #=> 7 h #=> {"dog"=>7} h[0] #=> "pig" h['cat'] #=> "pig" h[{:a=>1}] #=> "pig" h #=> {"dog"=>7} 

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

4而不是aw_down.each_char我们可以编写aw_down.chars.each ,但是aw_down.chars会创建一个不必要的中间数组。 each_char是一个枚举器,只是在需要时传递值。

5我们可以返回0而不是false1而不是true ,在这种情况下我们可以写

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

anagrams ,但是让anagrams返回一个值为truefalse的数组而不是01会不会更清楚?

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

 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 

无论如何,当简单数组与数组比较超快时,我不会担心部分比较。

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

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

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

 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的回答 。

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

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

产量

 0 0 1 1 
Interesting Posts