ย้อนกลับคำย้ายสระและลดทอนทุกอย่าง

Aug 19 2020

ฉันใหม่สำหรับ Ruby

ฟังก์ชันนี้ใช้สตริงของคำจำนวนเท่าใดก็ได้และกลับลำดับของคำ นอกจากนี้สำหรับแต่ละคำจะใช้สระและเลื่อนไปที่ท้ายคำ นอกจากนี้ยังลดลงทุกอย่าง ดังนั้นก็จะกลายเป็นHello World!wrld!o hlleo

ฉันพยายามใช้คุณสมบัติบางอย่างของ Ruby ด้วยเหตุนี้จึงเป็นหนึ่งซับเพื่อที่จะพูด โดยทั่วไปฉันแค่มองหาคำแนะนำสไตล์ เหมาะสมหรือไม่ที่จะทำสิ่งนี้ในลักษณะนี้ (หนึ่งบรรทัด?) ฉันแน่ใจว่ามีฟังก์ชั่นที่สามารถทำงานให้สำเร็จได้เร็วขึ้นดังนั้นฉันจึงเปิดรับข้อเสนอแนะเหล่านั้นเช่นกันเนื่องจากโค้ดของฉันยาวและซับซ้อนมาก นอกจากนี้ฉันควรพูดถึงฉันต้องการเขียนสิ่งนี้ด้วย Ruby พื้นฐานเท่านั้นไม่มีแพ็คเกจ / อัญมณีพิเศษ

มีคนแนะนำ Rubocop และ Style Guide ดังนั้นฉันจะตรวจสอบสิ่งเหล่านั้น

  def funky_words(s)
    s.strip.gsub(/\s+/, " ").split(" ").reverse.instance_eval{map{|elt| elt.gsub(/([aeiou])/i,"")}}.
    zip(s.strip.split(" ").reverse.map{|elt| elt.scan(/([aeiou])/i).flatten}.instance_eval{map{|elt| elt.join}}).
    map(&:join).join(" ").downcase
    #first "line" reverses word order removes vowels, second "line" captures vowels, last "line" joins vowels and all words
  end

คำตอบ

3 FMc Aug 26 2020 at 08:45

One-liners นั้นสนุกดี แต่โลกนี้ไม่ต้องการมันมากกว่านี้ ที่กล่าวว่าพวกเขาไม่จำเป็นต้องอ่านไม่ออก สมองในอนาคตของคุณจะพูดอะไรในอีกหนึ่งปีนับจากนี้หากคุณต้องรักษาการทำงานนั้นไว้

ต่อไปนี้เป็นแนวทางที่แสดงให้เห็นถึงเทคนิคที่สามารถปรับขนาดได้เพื่อให้ "one-liners" ยาว ๆ สามารถอ่านได้โดย (1) ใช้บรรทัดอย่างไม่เห็นแก่ตัว (2) การเยื้องโค้ดในลักษณะของโครงสร้างข้อมูลที่พิมพ์ออกมาสวยเพื่อถ่ายทอดลำดับชั้นของตรรกะ (รหัสคือข้อมูล, ท้ายที่สุด) และ (3) รวมถึงความคิดเห็นเพื่อช่วยผู้อ่านด้วยเหตุผลและเจตนา

def funky_words(s)
  (
    # Split into words.
    s
    .split
    .reverse_each
    .map { |word|
      # Within each word, push vowels to the end, while preserving
      # original order within consonants and vowels.
      word
      .each_char
      .sort_by.with_index { |c, i| "aeiouAEIOU".include?(c) ? [1, i] : [0, i] }
      .join
    }
    # Rejoin the new words.
    .join(" ")
  )
end
1 CarySwoveland Jan 19 2021 at 11:17

โซลูชันของคุณทำสิ่งต่อไปนี้:

  • downcase สตริง
  • แปลงสตริงผลลัพธ์เป็นอาร์เรย์ของคำ
  • ย้อนกลับอาร์เรย์ของคำ
  • แปลงคำแต่ละคำในอาร์เรย์เพื่อให้สระอยู่ท้ายสระและลำดับจะถูกเก็บรักษาไว้สำหรับทั้งสระและที่ไม่ใช่สระ
  • รวมคำในอาร์เรย์ผลลัพธ์เพื่อสร้างสตริง

เริ่มต้นด้วยการดำเนินการสุดท้าย เพื่อให้โค้ดสามารถอ่านได้มากขึ้นและการทดสอบความเร็วเรามาสร้างวิธีแยกกัน

VOWELS = 'aeiou'
def shove_vowels_to_end(word)
  vowels = ''
  non_vowels = ''
  word.each_char do |char|
     if VOWELS.include?(char)
       vowels << char
     else
       non_vowels << char
     end
  end
  [non_vowels, vowels].join
end

ดูString # each_char , String # include? และString # เข้าร่วม

นอกเหนือ: ฉันสามารถเขียนword.chars do |char|...แทนได้word.each_char do |char|...แต่ก่อนหน้านี้มีข้อเสียที่word.charsส่งคืนอาร์เรย์กลางในขณะที่ตัวหลังส่งคืนตัวแจงนับจึงใช้หน่วยความจำน้อยลง

ลองดู:

shove_vowels_to_end("atlastdisgonehurray!")
  #=> "tlstdsgnhrry!aaioeua"      

หากต้องการเราสามารถสร้างVOWELSชุด (เพื่อใช้Set # include?ซึ่งอาจทำให้การคำนวณเร็วขึ้น:

require 'set'

VOWELS = 'aeiou'.each_char.to_set
  #<Set: {"a", "e", "i", "o", "u"}>

ตอนนี้เราสามารถเขียนส่วนที่เหลือของวิธีการshove_vowels_to_end:

def funky_words(str)
  str.downcase.split.map { |word| shove_vowels_to_end(word) }.join(' ')
end

ฉันจะพูดคุยเกี่ยวกับรหัส แต่ก่อนอื่นเรามาลอง:

str = "Little Miss Muffett sat on her tuffet"

funky_words str
  #=> "lttlie mssi mffttue sta no hre tfftue"

ทั้งนี้ขึ้นอยู่กับสิ่งที่เป็นที่รู้จักกันเกี่ยวกับstrเราอาจจะต้องเปลี่ยนไปstr.split เหมือนกับซึ่งน่าจะเหมาะสม ดูString # แยกstr.strip.splitstr.splitstr.split(/\s+/)

การคำนวณระดับกลางคือ:

str.downcase.split.map { |word| shove_vowels_to_end(word) }
  #=> ["lttlie", "mssi", "mffttue", "sta", "no", "hre", "tfftue"]

ซึ่งเป็นเหตุผลที่เราต้องการ.join(' ')ในตอนท้าย

สังเกตว่าช่องว่างพิเศษจะไม่ถูกเก็บรักษาไว้:

funky_words "some       spaces"
  #=> "smoe spcsae"

นี่คือวิธีการเขียนที่เหมือนทับทิมมากขึ้นshove_vowels_to_end:

def shove_vowels_to_end(word)
  word.each_char.with_object(['', '']) do |char, (non_vowels, vowels)|
     if VOWELS.include?(char)
       vowels << char
     else
       non_vowels << char
     end
  end.join
end

ดูแจงนับ # with_object

สังเกตว่าฉันใช้การสลายอาร์เรย์เพื่อประโยชน์เมื่อเขียนตัวแปรบล็อก:

|char, (non_vowels, vowels)|

funky_wordsนี่คือวิธีการที่จะเขียนอีก ผมปรับเปลี่ยนลำดับของพื้นที่ที่ไม่ใช่แต่ละคนมีString # gsub

require 'set'
VOWELS = %w|a e i o u|.to_set
  #=> #<Set: {"a", "e", "i", "o", "u"}>
def funky_words(str)  
  str.downcase.gsub(/[^ ]+/) do |word|
    vowels = ''
    others = ''
    word.each_char do |char|
      if VOWELS.include?(char)
        vowels.prepend(char)
      else
        others.prepend(char)
      end
    end
    others + vowels
  end.reverse
end
str = "Little Miss Muffett sat on her tuffet"
funky_words(str)
  #=> "tfftue hre no sta mffttue mssi lttlie"

'muffett'พิจารณาปรับเปลี่ยนคำว่า 'mffttue'มันจะกลายเป็น อย่างไรก็ตามตั้งแต่ผมกลับสตริงที่สิ้นสุดที่ฉันจำเป็นต้องแปลงไป'muffett' 'muffett'.reverse #=> 'euttffm'ที่ได้รับในขั้นตอนต่อไปนี้:

muffett
vowels = ''
others = 'm'

uffett
vowels = 'u'
others = 'm'

ffett
vowels = 'u'
others = 'fm'

fett
vowels = 'u'
others = 'ffm'

ett
vowels = 'eu'
others = 'ffm

tt
vowels = 'eu'
others = 'tffm'

t
vowels = 'eu'
others = 'ttffm'

vowels + others
  #=> `euttffm`