在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}
(不需要检查!),这意味着这两个词是字谜,所以在这种情况下我们将返回true
的anagrams
。 5因此, 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=>1
, b=>2
, c=>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
而不是false
而1
而不是true
,在这种情况下我们可以写
a.zip(b).map { |aw,bw| anagram?(aw,bw) }
在anagrams
,但是让anagrams
返回一个值为true
或false
的数组而不是0
或1
会不会更清楚?
这不一定是火箭科学。 事实上,只要你能够始终如一地代表每个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