0

我有这个练习:

编写一个Title用字符串初始化的类。

它有一种方法——fix应该返回字符串的标题大小写版本:

Title.new("a title of a book").fix= 书名
您需要使用条件逻辑ifelse语句来完成这项工作。
确保您仔细阅读测试规范,以便了解要实现的条件逻辑。

您将要使用的一些方法:

String#downcase String#capitalize Array#include?

另外,这是 Rspec,我应该包括:

describe "Title" do
describe "fix" do
it "capitalizes the first letter of each word" do
  expect( Title.new("the great gatsby").fix ).to eq("The Great Gatsby")
end
it "works for words with mixed cases" do
  expect( Title.new("liTTle reD Riding hOOD").fix ).to eq("Little Red Riding Hood")
end
it "downcases articles" do
  expect( Title.new("The lord of the rings").fix ).to eq("The Lord of the Rings")
  expect( Title.new("The sword And The stone").fix ).to eq("The Sword and the Stone")
  expect( Title.new("the portrait of a lady").fix ).to eq("The Portrait of a Lady")
end
it "works for strings with all uppercase characters" do
  expect( Title.new("THE SWORD AND THE STONE").fix ).to eq("The Sword and the Stone")
end
end
end

谢谢@simone,我采纳了你的建议:

class Title
attr_accessor :string

def initialize(string)
@string = string
end

IGNORE = %w(the of a and)

def fix
s = string.split(' ')
s.map do |word|
  words = word.downcase
  if IGNORE.include?(word)
    words
  else
    words.capitalize
  end
end
s.join(' ')
end
end

虽然我在运行代码时仍然遇到错误:

expected: "The Great Gatsby"
 got: "the great gatsby"

(compared using ==)

exercise_spec.rb:6:in `block (3 levels) in <top (required)>' 

从我初学者的角度来看,我看不出我做错了什么?

最终编辑:我只想对大家早先为我提供帮助所付出的所有努力表示感谢。我将展示我能够生成的最终工作代码:

class Title
attr_accessor :string

def initialize(string)
@string = string
end

def fix
word_list = %w{a of and the}

a = string.downcase.split(' ')
b = []

a.each_with_index do |word, index|
  if index == 0 || !word_list.include?(word)
    b << word.capitalize
  else
    b << word
  end
end
b.join(' ')
end
end
4

4 回答 4

2

这是一个可能的解决方案。

class Title
  attr_accessor :string

  IGNORES = %w( the of a and )

  def initialize(string)
    @string = string
  end

  def fix
    tokens = string.split(' ')
    tokens.map do |token|
      token = token.downcase

      if IGNORES.include?(token)
        token
      else
        token.capitalize
      end
    end.join(" ")
  end

end

Title.new("a title of a book").fix

你的出发点很好。这里有一些改进:

  • 比较总是小写。这将简化 if 条件
  • 被忽略的项目列表被放入一个数组中。这将简化 if 条件,因为您不需要为每个忽略的字符串添加 if(它们可能是数百个)
  • 我使用地图来替换标记。使用带有枚举的块来循环项目是一种常见的 Ruby 模式
于 2015-02-02T19:56:55.150 回答
0

有两种方法可以解决此问题:

  • 将字符串分解成单词,可能修改每个单词并将单词重新组合在一起;或者
  • 使用正则表达式。

我会谈谈后者,但我相信你的练习涉及前者——这是你所采取的方法——所以我将专注于这一点。

将字符串拆分为单词

您用于String#split(' ')将字符串拆分为单词:

str = "a title of a\t   book"
a = str.split(' ')
  #=> ["a", "title", "of", "a", "book"] 

这很好,即使有额外的空格,但通常会这样写:

str.split
  #=> ["a", "title", "of", "a", "book"] 

两种方式都一样

str.split(/\s+/)
  #=> ["a", "title", "of", "a", "book"] 

请注意,我使用变量a来表示返回数组。有些人可能觉得描述性不够,但我相信它比 更好s,这有点令人困惑。:-)

创建枚举器

接下来,您发送Enumerable#each_with_index创建枚举器的方法:

enum0 = a.each_with_index
  # => #<Enumerator: ["a", "title", "of", "a", "book"]:each_with_index> 

要查看枚举器的内容,请转换enum0为数组:

enum0.to_a
  #=> [["a", 0], ["title", 1], ["of", 2], ["a", 3], ["book", 4]] 

您使用each_with_index了第一个词——带有索引的词——与其他词的0处理方式不同。没关系。

到目前为止,一切都很好,但此时您需要使用Enumerable#map将每个元素转换enum0为适当的值。例如,第一个值["a", 0]要转换为“A”,下一个要转换为“Title”,第三个要转换为“of”。

因此,您需要将方法发送Enumerable#mapenum0

enum1 = enum.map
  #=> #<Enumerator: #<Enumerator: ["a", "title", "of", "a",
        "book"]:each_with_index>:map> 
enum1.to_a
  #=> [["a", 0], ["title", 1], ["of", 2], ["a", 3], ["book", 4]] 

如您所见,这会创建一个新的枚举器,可以将其视为“复合”枚举器。

的元素enum1将通过 传递到块中Array#each

调用枚举器并加入

您希望将第一个单词和除以文章开头的单词之外的所有其他单词大写。因此,我们必须定义一些文章:

articles = %w{a of it} # and more
  #=> ["a", "of", "it"]

b = enum1.each do |w,i|
  case i
  when 0 then w.capitalize
  else articles.include?(w) ? w.downcase : w.capitalize
  end
end
  #=> ["A", "Title", "of", "a", "Book"] 

最后,我们在每个单词之间加入一个空格:

b.join(' ')
  => "A Title of a Book" 

查看计算的详细信息

让我们回到 的计算b。的第一个元素enum1被传递到块中并分配给块变量:

w, i = ["a", 0] #=> ["a", 0] 
w               #=> "a" 
i               #=> 0 

所以我们执行:

case 0
when 0 then "a".capitalize
else articles.include?("a") ? "a".downcase : "a".capitalize
end

返回"a".capitalize => "A"。同样,当 的下一个元素enum1传递给块时:

w, i = ["title", 1] #=> ["title", 1] 
w               #=> "title" 
i               #=> 1 

case 1
when 0 then "title".capitalize
else articles.include?("title") ? "title".downcase : "title".capitalize
end

它返回“标题”,因为articles.include?("title") => false。下一个:

w, i = ["of", 2] #=> ["of", 2] 
w               #=> "of" 
i               #=> 2 

case 2
when 0 then "of".capitalize
else articles.include?("of") ? "of".downcase : "of".capitalize
end

它返回“的”,因为articles.include?("of") => true

链接操作

综上所述,我们有:

str.split.each_with_index.map do |w,i|
  case i
  when 0 then w.capitalize
  else articles.include?(w) ? w.downcase : w.capitalize
  end
end
  #=> ["A", "Title", "of", "a", "Book"] 

替代计算

另一种不使用each_with_index的方法是这样的:

first_word, *remaining_words = str.split
first_word
  #=> "a" 
remaining_words
  #=> ["title", "of", "a", "book"] 

"#{first_word.capitalize} #{ remaining_words.map { |w|
  articles.include?(w) ? w.downcase : w.capitalize }.join(' ') }"
   #=> "A Title of a Book" 

使用正则表达式

str = "a title of a book"

str.gsub(/(^\w+)|(\w+)/) do
  $1 ? $1.capitalize :
    articles.include?($2) ? $2 : $2.capitalize
end
  #=> "A Title of a Book" 

正则表达式“捕获”[ (...)] 字符串 [ (^\w+)] 开头的单词或 [ |] 不一定在字符串 [ (\w+)] 开头的单词。两个捕获组的内容分别分配给全局变量$1$2

因此,遍历字符串的单词,第一个单词 ,"a"被捕获组 #1 捕获,因此(\w+)不被评估。每个后续单词都不会被捕获组#1 (so $1 => nil) 捕获,而是被捕获组#2 捕获。因此,如果$1is not nil,我们将(句子的)(第一个)单词大写;$2否则,如果单词不是文章,则大写,如果是文章,则保持不变。

于 2015-02-02T23:16:17.247 回答
0
def fix
   string.downcase.split(/(\s)/).map.with_index{ |x,i| 
     ( i==0 || x.match(/^(?:a|is|of|the|and)$/).nil? ) ? x.capitalize : x 
   }.join
end

满足所有条件:

  1. a , is , of , the和全部小写
  2. 将所有其他单词大写
  3. 所有第一个单词都大写

解释

  1. string.downcase调用一项操作以使您正在使用的字符串全部小写
  2. .split(/(\s)/)获取小写字符串并将其在空格(空格、制表符、换行符等)上拆分为一个数组,使每个单词成为数组的元素;括号中的\s(分隔符)也将其保留在返回的数组中,因此我们在重新加入时不会丢失那个空白字符
  3. .map.with_index{ |x,i|遍历返回的数组,其中x是值,i是索引号;每次迭代都返回一个新数组的元素;循环完成后,您将拥有一个新数组
  4. ( i==0 || x.match(/^(?:a|is|of|the|and)$/).nil? )如果它是数组中的第一个元素(索引为 0),或者单词匹配a, is, of,theand-- 即匹配不是nil-- 然后x.capitalize(将单词大写),否则(它确实匹配忽略的单词)所以只需返回单词/值,x
  5. .join获取我们的新数组并将所有单词再次组合成一个字符串

额外的

  • 通常,正则表达式中括号内的内容被认为是一个捕获组,这意味着如果里面的模式匹配,一个特殊的变量将在正则表达式操作完成后保留该值。在某些情况下,例如\s我们想要捕获该值,因为我们重用它,在其他情况下,例如我们忽略的单词,我们需要匹配,但不需要捕获它们。为避免捕获匹配项,您可以?:在捕获组的开头步调,告诉正则表达式引擎不要保留该值。这样做的许多好处超出了此答案的范围。
于 2015-02-02T22:33:32.767 回答
0

这是该问题的另一种可能的解决方案

class Title
  attr_accessor :str
  def initialize(str)
   @str = str
  end

  def fix
    s = str.downcase.split(" ") #convert all the strings to downcase and it will be stored in an array
    words_cap = []
    ignore = %w( of a and the ) # List of words to be ignored
    s.each do |item|
      if ignore.include?(item) # check whether word in an array is one of the words in ignore list.If it is yes, don't capitalize. 
        words_cap << item

      else
        words_cap << item.capitalize
      end  
    end
    sentence = words_cap.join(" ") # convert an array of strings to sentence
    new_sentence =sentence.slice(0,1).capitalize + sentence.slice(1..-1) #Capitalize first word of the sentence. Incase it is not capitalized while checking the ignore list.
  end


end    
于 2015-05-28T03:11:40.200 回答