在Ruby中删除字符串中的元音

Dan*_*Dan 3 ruby string replace

我正在测试以下问题的可能解决方案.我提出的"开膛"和"开膛机_2"的前两个解决方案运行不正常.我想找出原因.

Disembowel_3是迄今为止我最喜欢的解决方案.但是如果我不明白我的前两个解决方案出了什么问题,我觉得我无权使用disembowel_3.

谁能帮我弄清楚前两个解决方案有什么问题?

# Write a function disemvowel(string), which takes in a string,
# and returns that string with all the vowels removed. Treat "y" as a
# consonant.

def disemvowel(string)
  string_array = string.split
  vowels = %w[aeiou]
  i = 0
  while i < string.length
    if vowels.include? string[i] == true
      string_array[i] =  " "
    end
    i +=1
  end

  new_string = string_array.join
  new_string = new_string.sub(/\s+/,"")
  return new_string
end


def disemvowel_2(string)
  string_array = string.split('')
  string_array.delete('a','e','i','o','u')
  return string_array.join('')
end

# This is my favorite solution.
def disemvowel_3(string)
  result = string.gsub(/[aeiou]/i, '')
  return result
end


#tests
puts disemvowel("foobar") 
puts disemvowel("ruby") 
puts disemvowel("aeiou") 
Run Code Online (Sandbox Code Playgroud)

Mic*_*ill 5

轻微的改变会使dismvowel正常工作.这是固定的,为什么:

Disemvowel

臭虫

1) split改为string.split(""). split没有参数将按空格分割,并将split("")按字符分割.通过此更改,string_array字符串中每个字符的变为和数组.这也可以更简洁地完成string.chars,这是首选方法.

看到:

2) vowels被改成了一个字符串. %w[]创建一个单词的数组,所以在使用时%w[aeiou],vowels实际上是一个1字符串的数组"aeiou".这意味着既String#include?不会也Array#include?不会与每个角色进行比较.将其更改为常量字符串意味着vowels.include?可以匹配字符.

看到:

3) vowels.include?没有parens 明确地比较true.Ruby工作的方式,表达式的结果string_array[i] == true被传递给了vowels.include?,这不是预期的.

一些可以帮助解决这个问题的风格提示:

  • 与true的比较应该是隐含的(例如,不要使用== true)
  • 在调用函数或方法时使用parens.

看到:

4) sub改为gsub.调用sub只会在字符串中进行一次替换,因此在使用"fb r"调用时,只替换第一个空格,而保留字符串"fb r". gsub做"全局替换",这正是你想要的情况.

看到:

第一个工作版本

工作disemvowel函数如下所示:

def disemvowel(string)
  string_array = string.split("")
  vowels = "aeiou"
  i = 0
  while i < string.length
    if vowels.include?(string[i])
      string_array[i] =  " "
    end
    i +=1
  end

  new_string = string_array.join
  new_string = new_string.gsub(/\s+/,"")
  return new_string
end
Run Code Online (Sandbox Code Playgroud)

并使用您的测试生成此输出:

fbr
rby
Run Code Online (Sandbox Code Playgroud)

打扫干净

1)支持混合元音元音.

def disemvowel_1_1(string)
  string_array = string.split("")
  vowels = "aeiouAEIOU"
  i = 0
  while i < string_array.length
    if vowels.include?(string_array[i])
      string_array[i] =  " "
    end
    i +=1
  end

  new_string = string_array.join
  new_string = new_string.gsub(/\s+/,"")
  return new_string
end
Run Code Online (Sandbox Code Playgroud)

2)一致使用string_array而不是混合使用string.相反,string当它更适合使用时会发生各种用途string_array.这应该被替换.

def disemvowel_1_2(string)
  string_array = string.split("")
  vowels = "aeiouAEIOU"
  i = 0
  while i < string_array.length
    if vowels.include?(string_array[i])
      string_array[i] =  " "
    end
    i +=1
  end

  new_string = string_array.join
  new_string = new_string.gsub(/\s+/,"")
  return new_string
end
Run Code Online (Sandbox Code Playgroud)

3)不要将变量用于"aeiou".这是一个常量表达式,应该写成字符串文字或常量.在这种情况下,将选择一个文字字符串,因为没有封闭范围来限制在全局命名空间中使用常量(如果此代码插入到另一个上下文中).

def disemvowel_1_3(string)
  string_array = string.split("")
  i = 0
  while i < string_array.length
    if "aeiouAEIOU".include?(string_array[i])
      string_array[i] =  " "
    end
    i +=1
  end

  new_string = string_array.join
  new_string = new_string.gsub(/\s+/,"")
  return new_string
end
Run Code Online (Sandbox Code Playgroud)

4)替换元音字符nil而不是" "消除gsub替换.

def disemvowel_1_4(string)
  string_array = string.split("")
  i = 0
  while i < string_array.length
    if "aeiouAEIOU".include?(string_array[i])
      string_array[i] =  nil
    end
    i +=1
  end

  new_string = string_array.join
  return new_string
end
Run Code Online (Sandbox Code Playgroud)

5)转换while循环Array#each_with_index以处理数组元素

def disemvowel_1_5(string)
  string_array = string.split("")
  string_array.each_with_index do |char, i|
    if "aeiouAEIOU".include?(char)
      string_array[i] =  nil
    end
  end

  new_string = string_array.join
  return new_string
end
Run Code Online (Sandbox Code Playgroud)

6)替换使用split("")with String#chars来获取要处理的字符数组.

def disemvowel_1_6(string)
  string_array = string.chars
  string_array.each_with_index do |char, i|
    if "aeiouAEIOU".include?(char)
      string_array[i] =  nil
    end
  end

  new_string = string_array.join
  return new_string
end
Run Code Online (Sandbox Code Playgroud)

7)通过链接结果减少临时变量的数量.这可以最小化Ruby必须跟踪的单个变量的数量,并减少每次引用变量名时发生的变量查找.

def disemvowel_1_7(string)
  string_array = string.chars
  string_array.each_with_index do |char, i|
    if "aeiouAEIOU".include?(char)
      string_array[i] =  nil
    end
  end

  new_string = string_array.join
  return new_string
end
Run Code Online (Sandbox Code Playgroud)

8)删除显式返回以使用Ruby的基于表达式的返回值.

def disemvowel_1_8(string)
  string_array = string.chars
  string_array.each_with_index do |char, i|
    if "aeiouAEIOU".include?(char)
      string_array[i] =  nil
    end
  end.join
end
Run Code Online (Sandbox Code Playgroud)

9)使用Array#map处理字符,而不是Array#each_with_index.

def disemvowel_1_9(string)
  string.chars.map {|char| "aeiouAEIOU".include?(char) ? nil : char }.join
end
Run Code Online (Sandbox Code Playgroud)

Disemvowel 2

臭虫

1)替换deletedelete_if.该Array#delete方法仅删除完全匹配,因此在这种情况下,您必须循环元组以使其正常工作.但是,Array#delete_if使您能够在条件上删除,并且条件是vowels.include?(element).

看到:

第一个工作版本

def disemvowel_2(string)
  string_array = string.split('')
  string_array.delete_if {|element| "aeiou".include?(element) }
  string_array.join('')
end 
Run Code Online (Sandbox Code Playgroud)

打扫干净

1)支持混合元音元音.

def disemvowel_2_1(string)
  string_array = string.split('')
  string_array.delete_if {|element| "aeiouAEIOU".include?(element) }
  string_array.join('')
end
Run Code Online (Sandbox Code Playgroud)

2)替换使用split("")with String#chars来获取要处理的字符数组.

def disemvowel_2_2(string)
  string_array = string.chars
  string_array.delete_if {|element| "aeiouAEIOU".include?(element) }
  string_array.join('')
end
Run Code Online (Sandbox Code Playgroud)

3)join('')改为just join.该join方法已经以这种方式加入,因此额外的参数是多余的

def disemvowel_2_3(string)
  string_array = string.chars
  string_array.delete_if {|element| "aeiouAEIOU".include?(element) }
  string_array.join('')
end
Run Code Online (Sandbox Code Playgroud)

4)通过链接结果减少临时变量的数量.这可以最小化Ruby必须跟踪的单个变量的数量,并减少每次引用变量名时发生的变量查找.

def disemvowel_2_4(string)
  string.chars.delete_if {|element| "aeiouAEIOU".include?(element) }.join
end
Run Code Online (Sandbox Code Playgroud)

Disemvowel 4

String有一个delete方法可以删除所有匹配的字符.鉴于元音,这是一个简单的实现:

def disemvowel_4(string)
  string.delete("aeiouAEIOU")
end
Run Code Online (Sandbox Code Playgroud)

看到:

测试

我创建了一个像程序一样的单元测试来进行程序化自我测试,而不是仅仅将不受约束的字符串显示到控制台.这将测试函数的每个版本并报告它是否通过测试:

data = [
  ["foobar", "fbr"],
  ["ruby", "rby"],
  ["aeiou", ""],
  ["AeIoU", ""],
]

data.each do |test|
  puts "disemvowel_1   #{disemvowel_1(test[0]) == test[1] ? 'Pass' : 'Fail'}: '#{test[0]}'"
  puts "disemvowel_1_1 #{disemvowel_1_1(test[0]) == test[1] ? 'Pass' : 'Fail'}: '#{test[0]}'"
  puts "disemvowel_1_2 #{disemvowel_1_2(test[0]) == test[1] ? 'Pass' : 'Fail'}: '#{test[0]}'"
  puts "disemvowel_1_3 #{disemvowel_1_3(test[0]) == test[1] ? 'Pass' : 'Fail'}: '#{test[0]}'"
  puts "disemvowel_1_4 #{disemvowel_1_4(test[0]) == test[1] ? 'Pass' : 'Fail'}: '#{test[0]}'"
  puts "disemvowel_1_5 #{disemvowel_1_5(test[0]) == test[1] ? 'Pass' : 'Fail'}: '#{test[0]}'"
  puts "disemvowel_1_6 #{disemvowel_1_6(test[0]) == test[1] ? 'Pass' : 'Fail'}: '#{test[0]}'"
  puts "disemvowel_1_7 #{disemvowel_1_7(test[0]) == test[1] ? 'Pass' : 'Fail'}: '#{test[0]}'"
  puts "disemvowel_1_8 #{disemvowel_1_8(test[0]) == test[1] ? 'Pass' : 'Fail'}: '#{test[0]}'"
  puts "disemvowel_1_9 #{disemvowel_1_9(test[0]) == test[1] ? 'Pass' : 'Fail'}: '#{test[0]}'"
  puts "disemvowel_2   #{disemvowel_2(test[0]) == test[1] ? 'Pass' : 'Fail'}: '#{test[0]}'"
  puts "disemvowel_2_1 #{disemvowel_2_1(test[0]) == test[1] ? 'Pass' : 'Fail'}: '#{test[0]}'"
  puts "disemvowel_2_2 #{disemvowel_2_2(test[0]) == test[1] ? 'Pass' : 'Fail'}: '#{test[0]}'"
  puts "disemvowel_2_3 #{disemvowel_2_3(test[0]) == test[1] ? 'Pass' : 'Fail'}: '#{test[0]}'"
  puts "disemvowel_2_4 #{disemvowel_2_4(test[0]) == test[1] ? 'Pass' : 'Fail'}: '#{test[0]}'"
  puts "disemvowel_3   #{disemvowel_3(test[0]) == test[1] ? 'Pass' : 'Fail'}: '#{test[0]}'"
  puts "disemvowel_4   #{disemvowel_4(test[0]) == test[1] ? 'Pass' : 'Fail'}: '#{test[0]}'"
end
Run Code Online (Sandbox Code Playgroud)

这将产生以下输出:

>$ ruby disemvowel.rb
disemvowel_1   Pass: 'foobar'
disemvowel_1_1 Pass: 'foobar'
disemvowel_1_2 Pass: 'foobar'
disemvowel_1_3 Pass: 'foobar'
disemvowel_1_4 Pass: 'foobar'
disemvowel_1_5 Pass: 'foobar'
disemvowel_1_6 Pass: 'foobar'
disemvowel_1_7 Pass: 'foobar'
disemvowel_1_8 Pass: 'foobar'
disemvowel_1_9 Pass: 'foobar'
disemvowel_2   Pass: 'foobar'
disemvowel_2_1 Pass: 'foobar'
disemvowel_2_2 Pass: 'foobar'
disemvowel_2_3 Pass: 'foobar'
disemvowel_2_4 Pass: 'foobar'
disemvowel_3   Pass: 'foobar'
disemvowel_4   Pass: 'foobar'
disemvowel_1   Pass: 'ruby'
disemvowel_1_1 Pass: 'ruby'
disemvowel_1_2 Pass: 'ruby'
disemvowel_1_3 Pass: 'ruby'
disemvowel_1_4 Pass: 'ruby'
disemvowel_1_5 Pass: 'ruby'
disemvowel_1_6 Pass: 'ruby'
disemvowel_1_7 Pass: 'ruby'
disemvowel_1_8 Pass: 'ruby'
disemvowel_1_9 Pass: 'ruby'
disemvowel_2   Pass: 'ruby'
disemvowel_2_1 Pass: 'ruby'
disemvowel_2_2 Pass: 'ruby'
disemvowel_2_3 Pass: 'ruby'
disemvowel_2_4 Pass: 'ruby'
disemvowel_3   Pass: 'ruby'
disemvowel_4   Pass: 'ruby'
disemvowel_1   Pass: 'aeiou'
disemvowel_1_1 Pass: 'aeiou'
disemvowel_1_2 Pass: 'aeiou'
disemvowel_1_3 Pass: 'aeiou'
disemvowel_1_4 Pass: 'aeiou'
disemvowel_1_5 Pass: 'aeiou'
disemvowel_1_6 Pass: 'aeiou'
disemvowel_1_7 Pass: 'aeiou'
disemvowel_1_8 Pass: 'aeiou'
disemvowel_1_9 Pass: 'aeiou'
disemvowel_2   Pass: 'aeiou'
disemvowel_2_1 Pass: 'aeiou'
disemvowel_2_2 Pass: 'aeiou'
disemvowel_2_3 Pass: 'aeiou'
disemvowel_2_4 Pass: 'aeiou'
disemvowel_3   Pass: 'aeiou'
disemvowel_4   Pass: 'aeiou'
disemvowel_1   Fail: 'AeIoU'
disemvowel_1_1 Pass: 'AeIoU'
disemvowel_1_2 Pass: 'AeIoU'
disemvowel_1_3 Pass: 'AeIoU'
disemvowel_1_4 Pass: 'AeIoU'
disemvowel_1_5 Pass: 'AeIoU'
disemvowel_1_6 Pass: 'AeIoU'
disemvowel_1_7 Pass: 'AeIoU'
disemvowel_1_8 Pass: 'AeIoU'
disemvowel_1_9 Pass: 'AeIoU'
disemvowel_2   Pass: 'AeIoU'
disemvowel_2_1 Pass: 'AeIoU'
disemvowel_2_2 Pass: 'AeIoU'
disemvowel_2_3 Pass: 'AeIoU'
disemvowel_2_4 Pass: 'AeIoU'
disemvowel_3   Pass: 'AeIoU'
disemvowel_4   Pass: 'AeIoU'
Run Code Online (Sandbox Code Playgroud)

标杆

我编写了一个基准程序来测试每个实现的性能.这是基准程序:

Times = 5_000
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(),./<>?;':\"[]{}\\|-=_+`~".chars
array = Times.times.map { |n| "#{chars.sample(n)}" }

puts "============================================================="
puts RUBY_DESCRIPTION

Benchmark.bm(15) do |x|
  dismevowel_1_report =   x.report("disemvowel_1:")   { array.each {|s| disemvowel_1(s) } }
  dismevowel_1_1_report = x.report("disemvowel_1_1:") { array.each {|s| disemvowel_1_1(s) } }
  dismevowel_1_2_report = x.report("disemvowel_1_2:") { array.each {|s| disemvowel_1_1(s) } }
  dismevowel_1_3_report = x.report("disemvowel_1_3:") { array.each {|s| disemvowel_1_1(s) } }
  dismevowel_1_4_report = x.report("disemvowel_1_4:") { array.each {|s| disemvowel_1_1(s) } }
  dismevowel_1_5_report = x.report("disemvowel_1_5:") { array.each {|s| disemvowel_1_1(s) } }
  dismevowel_1_6_report = x.report("disemvowel_1_6:") { array.each {|s| disemvowel_1_1(s) } }
  dismevowel_1_7_report = x.report("disemvowel_1_7:") { array.each {|s| disemvowel_1_1(s) } }
  dismevowel_1_8_report = x.report("disemvowel_1_8:") { array.each {|s| disemvowel_1_1(s) } }
  dismevowel_1_9_report = x.report("disemvowel_1_9:") { array.each {|s| disemvowel_1_1(s) } }
  dismevowel_2_report   = x.report("disemvowel_2:")   { array.each {|s| disemvowel_2(s) } }
  dismevowel_2_1_report = x.report("disemvowel_2_1:") { array.each {|s| disemvowel_1_1(s) } }
  dismevowel_2_2_report = x.report("disemvowel_2_2:") { array.each {|s| disemvowel_1_1(s) } }
  dismevowel_2_3_report = x.report("disemvowel_2_3:") { array.each {|s| disemvowel_1_1(s) } }
  dismevowel_2_4_report = x.report("disemvowel_2_4:") { array.each {|s| disemvowel_1_1(s) } }
  dismevowel_3_report   = x.report("disemvowel_3:")   { array.each {|s| disemvowel_3(s) } }
  dismevowel_4_report   = x.report("disemvowel_4:")   { array.each {|s| disemvowel_4(s) } }
end
Run Code Online (Sandbox Code Playgroud)

这是基准测试的输出:

=============================================================
ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14]
                      user     system      total        real
disemvowel_1:     2.630000   0.010000   2.640000 (  3.487851)
disemvowel_1_1:   2.300000   0.010000   2.310000 (  2.536056)
disemvowel_1_2:   2.360000   0.010000   2.370000 (  2.651750)
disemvowel_1_3:   2.290000   0.010000   2.300000 (  2.449730)
disemvowel_1_4:   2.320000   0.020000   2.340000 (  2.599105)
disemvowel_1_5:   2.360000   0.010000   2.370000 (  2.473005)
disemvowel_1_6:   2.340000   0.010000   2.350000 (  2.813744)
disemvowel_1_7:   2.380000   0.030000   2.410000 (  3.663057)
disemvowel_1_8:   2.330000   0.010000   2.340000 (  2.525702)
disemvowel_1_9:   2.290000   0.010000   2.300000 (  2.494189)
disemvowel_2:     2.490000   0.000000   2.490000 (  2.591459)
disemvowel_2_1:   2.310000   0.010000   2.320000 (  2.503748)
disemvowel_2_2:   2.340000   0.010000   2.350000 (  2.608350)
disemvowel_2_3:   2.320000   0.010000   2.330000 (  2.820086)
disemvowel_2_4:   2.330000   0.010000   2.340000 (  2.735653)
disemvowel_3:     0.070000   0.000000   0.070000 (  0.070498)
disemvowel_4:     0.020000   0.000000   0.020000 (  0.018580)
Run Code Online (Sandbox Code Playgroud)

结论

String#delete方法大大优于所有手卷解决方案,除了String#gsub超过100倍,并且比其快2.5倍String#gsub.它非常易于使用,并且优于其他一切; 这是最好的解决方案.