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

2023-12-20

如果我的代码已关闭,请原谅我。我仍然对 Ruby on Rails 很感兴趣,随着我了解更多“只是 Ruby”,似乎存在一些细微的差异,尽管公平地说,我不确定我的代码是否能通过 Ruby on Rails 格式的测试。我离题了。

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

在下面的例子中,我want要打印的是:

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

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

您可以考虑按如下方式进行操作。

Code

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

Example

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

解释

anagrams method

对于上面的例子,

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

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

Next,

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#map http://www.ruby-doc.org/core-2.1.1/Enumerable.html#method-i-map passes each element of c (a two-element array) into the block.1. It is clearer, however, to decompose (or "disambiguate") those arrays and assign each of the two words they comprise to a block variable2:

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

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

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

我们执行

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

这是简写

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

anagram? method

现在让我们继续anagram?, with

aw = "hello"
bw = "helio"

Since

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}

(最后一行只是为了显示哈希值。)

In the first line we create a hash counts with a default value of zero. All this means is that if counts does not contain the key k, counts[k] will return the default value. Very important: doing so does not change the hash!3

String#each_char http://www.ruby-doc.org/core-2.1.3/String.html#method-i-each_char4 passes each character of "hello" into the block and assigns it to the block variable c. Initially, c='h' and h={}. We then execute

counts['h'] += 1

这是简写

counts['h'] = counts['h'] + 1

Since 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}

方法可枚举#each_with_object http://www.ruby-doc.org/core-2.1.1/Enumerable.html#method-i-each_with_object会成为好朋友

使用该方法只是为了节省一些步骤。它允许我们写:

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

as

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类方法允许方法链接,这是非常有用的。

减少第二个单词中字母的字母计数

Recall

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

First, 'h'被传递到块中。作为counts['h'] #=> 1,我们执行counts['h'] -= 1, so now

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

通过后'e' and 'l'到街区,

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

但当我们经过时'i', 我们发现

counts['i'] #=> 0

(即返回默认值零,并且我们不想设置counts['i'] to -1)所以我们返回false,得出的结论是这两个词不是字谜。 (如果第二个词是"heeio",我们会回来的false当第二个'e'被传递到块。)

我们有字谜吗?

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

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

(no need to check!), meaning the two words are anagrams, so in this case we would return true to anagrams.5 Hence, the last line of anagram?.

Notes

1 Under the hood, this is what's happening:

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

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

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

其实就是这个方法数组#each http://www.ruby-doc.org/core-2.1.1/Array.html#method-i-each传递的元素enum进入块:

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

2 If we pass [[1,2],3] into a block, and the block variables are written |(a,b),c|, then a=>1, b=>2, c=>3. This is quite handy. Cool, eh?.

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#new http://www.ruby-doc.org/core-2.1.1/Hash.html它采用块,允许在引用不在哈希中的键时添加它们。

4 Instead of aw_down.each_char we could have written aw_down.chars.each, but aw_down.chars creates an unnecessary intermediate array. each_char, an enumerator, merely passes values as they are required.

5 We could return 0 rather than false and 1 rather than true, in which case we could write

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

in anagrams,但是如果有的话不是更清楚吗anagrams返回一个数组,其值为true or false, 而不是0 or 1?

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Ruby 中比较两个包含字谜字符串的数组 的相关文章

随机推荐