比较包含字符串在Ruby中字谜两个数组 [英] Comparing two arrays containing strings for anagrams in Ruby

查看:235
本文介绍了比较包含字符串在Ruby中字谜两个数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

原谅我,如果我的code是关闭。我还有我的头上的Ruby on Rails的,这似乎已经被说成我学习更细微的差别只是红宝石,虽然是公平的,我不知道我的code将通过鼓起一个Ruby on Rails的格式。我离题了。

我想比较包含一组字符串的两个数组。我想要做的几件事情。 1)确保阵列相同数目的话,否则运动是没有实际意义。 2)在阵列进行比较的第一个字的只有的第二个数组中的第一个字。换句话说,我从来没有想在阵是与阵列B字4比较字1。我挣扎地发现,重新排序在任何给定的字中的字符,它比较在所述第二阵列中的相应的字的重新排序字符的溶液中,并打印1,如果它是一个字谜(一次排序,这个想法是这两个词将等价)或0,如果它们不匹配

在下面的例子中,我的希望的它打印的是:

0结果
0结果
1结果
1

...但是,这没有发生。思考?这恐怕与局部变量的问题做的,但我不能肯定。

  A = ['你好','再见','裤','咩']
    B = ['的Helio','godbye','spant','ABA']    X =则为a.length
    Y = b.length个
    Z = 0    X = Y?做
        而z,其中, X千万
            如果a.find(Z).chars.sort.join == b.find(Z).chars.sort.join
                把1
            其他
                会将0
            结束            Z + = 1
        结束
    结束


解决方案

[修改的:我已经编辑我的答案以纳入@raph中建议提高能效关于这个问题的意见(该方法字谜?下面)。这可能不是必要的,但我认为这是它应该得到一些曝光一个好主意。我也给出了详细的解释,因为OP是新的红宝石,因为可能有其他的读者。]

您可能会考虑做如下:

code

  DEF字谜(A,B)
  返回nil,除非a.size == b.size
  a.zip(二).MAP {| AW,BW |字谜?(AW,BW)? 1:0}
结束高清字谜?(AW,BW)
  返回false,除非aw.size == bw.size
  数= aw.downcase.each_char.with_object(Hash.new(0)){| C,H | H [J] + = 1}
  bw.downcase.each_char做| C |
    返回false,除非计数[C]> 0
    计数[C] - = 1
  结束
  真正
结束

示例

  A = ['你好','再见','裤','咩']
B = ['的Helio','godbye','Spant','ABA']
字谜(A,B)
  #=> [0,0,1,1]

说明

字谜

有关上述的例子中,

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

所以我们没有在字谜的第一行返回

接下来,

  C = a.zip(二)
  #=> [你好,Helio公司],[再见,godb​​ye],
  #[裤,Spant],[咩,阿坝]]

假设为字谜片刻和根据需要:

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

可枚举#地图通行证的每个元素C (包含两个元素的数组)成块。 1 。它是更明确的,但是,以分解(或消歧)的那些阵列,并分配各两个词的它们包括到块变量 2

  c.map {| AW,BW |字谜?(AW,BW)? 1:0}
  #=> [0,0,1,1]

在传递的第一个元素是 [你好,Helio公司] ,所以

  AW = GT; 你好
BW#=> Helio公司

和我们执行

 字谜?(你好,Helio公司)? 1:0
  #=> 0

这是简写

 如果字谜?(你好,Helio公司)
  1
其他
  0
结束
  #=> 0

字谜?

所以,现在让我们进入到字谜?

  AW =你好
体重=Helio公司

由于

  aw.size == bw.size#=>真正

我们不回。

中的第一个字的字母计数频率

让我写信字谜接下来的几行略有不同:

 计数= Hash.new(0)
  #=> {}
aw_down = aw.downcase
  #=> 你好
aw_down.each_char {| C |计数[C] + = 1}
  #=> 你好
计数
  #=> {的h=大于1,E=大于1,L=大于2,O=大于1}

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

在第一行我们创建了一个哈希计数为零的默认值。所有这一切意味着,如果计数不包含关键 K 计数[K] 将返回默认值。 很重要: 这样做不会改变散列 3

串#each_char < SUP> 4 经过的每一个字符你好成块,并将其分配给块变量 C 。最初, C ='H' H = {} 。然后,我们执行

 计数['H'] + = 1

这是简写

 计数['H'] =计数['H'] + 1

由于计数还没有一个键'H'计数[' H'] 右边返回默认值:

 计数['H'] = 0 + 1#=&GT; 1
计数#=&GT; {的h=大于1}

同样,'E'和第一后L被传递到块,我们有:

 计数#=&GT; {的h=大于1,E=大于1,L=大于1}

然而,当我们通过第二个L,我们执行

 计数['L'] =计数['L'] + 1
  #=&GT; 1 + 1
  #=&GT; 2

和我们完成了

 计数#=&GT; {的h=大于1,E=大于1,L=大于2,O=大于1}

的方法可枚举#each_with_object 将成为一个很好的朋友

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

 计数= Hash.new(0)
aw_down.each_char {| C |计数[C] + = 1}

 计数= aw_down.each_with_object(Hash.new(0)){| C,H | H [J] + = 1}

和我们也可以摆脱线的

  aw_down = aw.downcase

 计数= aw.downcase.each_char.with_object(Hash.new(0)){| C,H | H [J] + = 1}

这可能看起来像一个小的储蓄,但也有很多其他的情况下使用 each_with_object 可枚举类方法允许方法的链接,这是非常有用的。

递减计数信在第二个字字母

回顾

 计数#=&GT; {的h=大于1,E=大于1,L=大于2,O=大于1}

我们现在执行

  bw_down = bw.downcase
  #=&GT; Helio公司
Helio公司.each_char做| C |
  返回false,除非计数[C]&GT; 0
  计数[C] - = 1
结束

首先,'H'传递到块。由于计数['H']#=&GT; 1 ,我们执行计数['H'] - = 1 ,所以现在

 计数#=&GT; {H=大于0,E=大于1,L= GT; 2,O=&GT; 1}`。

通过后'E'L来将挡,

 计数#=&GT; {的h=大于0,E=大于0,L=大于1,○=大于1}

但是,当我们通过,我们发现

 计数['I']#=&GT; 0

(即返回的默认值零,我们不希望设置计数['我'] -1 ),所以我们返回在签订这两个词都没有字谜。 (有第二个字被heeio,我们将返回当第二 'E'传递给该块。)

我们有一个字谜?

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

 计数#=&GT; {的h=大于0,E=大于0,L=大于0,O=大于0}

(无需检查!),这意味着两个字字谜,所以在这种情况下,我们将返回真正字谜 5 因此,字谜的最后一行?

备注

1 引擎盖下,这是发生了什么:

 枚举= c.map
  #=&GT; #&LT;枚举:[[你好,Helio公司],[再见,godb​​ye],
  #[裤,Spant],[咩,阿坝]]:地图&GT;

在这里我们可以看到普查员将传递到块什么样的元素,但有时你需要枚举转换为数组来获取信息:

  enum.to_a
  #=&GT; [你好,Helio公司],[再见,godb​​ye],
  #[裤,Spant],[咩,阿坝]]

这实际上是方法阵列每个# 传递枚举的元素成块:

  enum.each {| AW,BW |字谜?(AW,BW)? 1:0}
  #=&GT; [0,0,1,1]

2 如果我们通过 [1,2],3] 成块和块变量被写入 |(A,b),C | ,那么 A =&GT; 1 b =&GT; 2 C =&GT; 3 。这是很方便。酷,不是吗?

3

  H = Hash.new('猪')
H ['狗'] = 7#=&GT; 7
^ h#=&GT; {狗=大于7}
H [0]#=&GT; 猪
H ['猫']#=&GT; 猪
H [{:一个=大于1}]#= GT; 猪
^ h#=&GT; {狗=大于7}

请注意存在的哈希#新的形式,当它们被引用被添加花费块,这使得键不是在散列

4 相反,我们可以这样写的 aw_down.each_char aw_down.chars.each ,但 aw_down.chars 创建一个不必要的中间阵列。 each_char ,枚举,因为他们只需要传递的值。

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

  a.zip(二).MAP {| AW,BW |字谜?(AW,BW)}

字谜,但不会是更清晰的有 字谜返回一个数组,其值真正,而不是 0 1

Forgive me if my code is off. I've still got my head in Ruby on Rails which seems to have subtle differences that are coming out as I learn more "just Ruby," although to be fair I'm not sure my code would pass muster in a Ruby on Rails format. I digress.

I am trying to compare two arrays that contain a set of strings. I want to do a couple things. 1) Ensure that the arrays are the same number of words, otherwise the exercise is moot. 2) Compare the first word in an array with only the first word in the second array. In other words, I never want to compare word 1 in array "a" with word 4 in array "b". I'm struggling to find a solution that reorders the characters in any given word, compares it to the reordered characters in the corresponding word in the second array, and prints 1 if it's an anagram (once sorted, the idea is that the two words would be equivalent) or 0 if they do not match.

In the example below, what I want it to print is:

0
0
1
1

...but that is not happening. Thoughts? I'm afraid this has to do with local variable issues, but I am not be sure.

    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: I've edited my answer to incorporate an efficiency improvement suggested by @raph in a comment on the question (the method anagram? below). That may not be necessary, but I thought it was such a good idea that it should get some exposure. I've also given a detailed explanation, as the OP is new to Ruby, as might be other readers.]

You might consider doing it as follows.

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]

Explanation

anagrams method

For the example above,

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

so we don't return nil in the first line of anagrams.

Next,

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

Assuming for a moment that anagram? works as desired:

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

Enumerable#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]

The first element passed in is ["hello", "helio"], so

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

and we execute

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

which is shorthand for

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

anagram? method

So now let's move on to anagram?, with

aw = "hello"
bw = "helio"

Since

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

we don't return.

Count frequency of letters in the first word

Let me now write the next few lines of anagram? slightly differently:

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}

(The last line is there just to show the value of the hash.)

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_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

which is shorthand for

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

Since counts does not yet have a key 'h', counts['h'] on the right returns the default value:

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

Similarly, after 'e' and the first 'l' are passed to the block, we have:

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

However, when we pass the second 'l', we execute

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

and we finish up with

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

The method Enumerable#each_with_object will become a good friend

This method is used merely to save some steps. It allows us to write:

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 }

and we can also get rid of the line

aw_down = aw.downcase 

by writing

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

This may seem like a small saving, but there are many other situations where the use of each_with_object and other Enumerable class methods permit the chaining of methods, which is extremely useful.

Decrementing letter counts for letters in the second word

Recall

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

We now execute

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

First, 'h' is passed into the block. As counts['h'] #=> 1, we execute counts['h'] -= 1, so now

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

After passing 'e' and 'l' to the block,

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

but when we pass 'i', we find

counts['i'] #=> 0

(i.e., the default value of zero is returned, and we don't want to set counts['i'] to -1) so we return false, having concluded that the two words are not anagrams. (Had the second word been "heeio", we would have returned false when the second 'e' was passed to the block.)

Do we have an anagram?

Since two two words have the same length, if we are able to process all characters of the second word without returning false, we must end up with

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>

Here we can see what elements the enumerator will pass into the block, but sometimes you need to convert the enumerator to an array to get that information:

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

It is actually the method Array#each that passes the elements of enum into the block:

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}

Note there is a form of Hash#new that takes block, which allows keys not in the hash to be added when they are referenced.

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, but wouldn't it be clearer to have anagrams return an array whose values are true or false, rather than 0 or 1?

这篇关于比较包含字符串在Ruby中字谜两个数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆