odd rake behavior - is it broken? - ruby

EDIT: figured it out. This is telling me my ruby is producing an infinite loop. Now if I could only figure out how to fix the loop ...
I run a rake test and this is all that outputs to the terminal:
(in /home/macs/Desktop/projects/odin/odin3_ruby/learn_ruby)
#translate
The test was working fine before I changed my ruby to this:
def translate(x)
vowel = /\b[aeiou]*/
array = x.split("")
until array[0]==vowel do
it = array[0]
array.push(it)
array.delete(it)
end
new = array.join("")
new+="ay"
end
I don't remember exactly what my ruby code was before I changed it.
If you are curious to see my rake file it is this. By the way I downloaded this file from the tutorial website and I am positive I didn't change it at all.
# # Topics
#
# * modules
# * strings
#
# # Pig Latin
#
# Pig Latin is a made-up children's language that's intended to be confusing. It obeys a few simple rules (below) but when it's spoken quickly it's really difficult for non-children (and non-native speakers) to understand.
#
# Rule 1: If a word begins with a vowel sound, add an "ay" sound to the end of the word.
#
# Rule 2: If a word begins with a consonant sound, move it to the end of the word, and then add an "ay" sound to the end of the word.
#
# (There are a few more rules for edge cases, and there are regional variants too, but that should be enough to understand the tests.)
#
# See <http://en.wikipedia.org/wiki/Pig_latin> for more details.
#
#
require "pig_latin"
describe "#translate" do
it "translates a word beginning with a vowel" do
s = translate("apple")
s.should == "appleay"
end
it "translates a word beginning with a consonant" do
s = translate("banana")
s.should == "ananabay"
end
it "translates a word beginning with two consonants" do
s = translate("cherry")
s.should == "errychay"
end
it "translates two words" do
s = translate("eat pie")
s.should == "eatay iepay"
end
it "translates a word beginning with three consonants" do
translate("three").should == "eethray"
end
it "counts 'sch' as a single phoneme" do
s = translate("school")
s.should == "oolschay"
end
it "counts 'qu' as a single phoneme" do
s = translate("quiet")
s.should == "ietquay"
end
it "counts 'qu' as a consonant even when it's preceded by a consonant" do
s = translate("square")
s.should == "aresquay"
end
it "translates many words" do
s = translate("the quick brown fox")
s.should == "ethay ickquay ownbray oxfay"
end
# Test-driving bonus:
# * write a test asserting that capitalized words are still capitalized (but with a different initial capital letter, of course)
# * retain the punctuation from the original phrase
end

You have an array of Strings:
array = x.split("")
but you're comparing those strings with a Regexp:
vowel = /\b[aeiou]*/
#...
until array[0]==vowel do
a == b is false for every String a and Regexp b so you're just writing this in a complicated way:
until false do
Perhaps you mean to say:
until array[0] =~ vowel do
That should stop your infinite loop but your loop still doesn't make much sense. You do this:
it = array[0]
array.push(it)
array.delete(it)
So you grab the first element, push it onto the end of the array, and then delete everything from array that matches it (note that Array#delete deletes all matches). Why not just array.delete(it)? Or, if you only want to toss the first entry, use shift.

Related

Pig Latin exercise works, but only for one user inputed word. Not all words

I'm new to programming and I'm working with Ruby as my starter language. The below code works, but if someone inputs more than one word, the pigatize method only works on the first word and adds the additional ay or way to the last word. How do i get it to apply to each word a user inputs?
# If the first letter is a vowel, add "way" to the end
# If the first letter is a consonant, move it to the end and add "ay"
class PigLatin
VOWELS = %w(a e i o u)
def self.pigatize(text)
if PigLatin.vowel(text[0])
pigalatin = text + 'way'
else
piglatin = text[1..-1] + text[0] + 'ay'
end
end
def self.vowel(first_letter)
VOWELS.include?(first_letter)
end
end
puts 'Please enter a word and I will translate it into Pig Latin. Ippyyay!.'
text = gets.chomp
puts "Pigatized: #{PigLatin.pigatize(text)}"
Chiefly, you need to split the input string into words with String#split, using an expression like:
text.split(' ')
That produces an array of words, which you can loop over with an .each block and run the algorithm on each word, then reassemble them with += and a space at the end + ' '
Incorporating these things into your existing code looks like the following (with comments):
class PigLatin
VOWELS = %w(a e i o u)
def self.pigatize(text)
# Declare the output string
piglatin = ''
# Split the input text into words
# and loop with .each, and 'word' as the iterator
# variable
text.split(' ').each do |word|
if PigLatin.vowel(word[0])
# This was misspelled...
# Add onto the output string with +=
# and finish with an extra space
piglatin += word + 'way' + ' '
else
# Same changes down here...
piglatin += word[1..-1] + word[0] + 'ay' + ' '
end
end
# Adds a .chomp here to get rid of a trailing space
piglatin.chomp
end
def self.vowel(first_letter)
VOWELS.include?(first_letter)
end
end
puts 'Please enter a word and I will translate it into Pig Latin. Ippyyay!.'
text = gets.chomp
puts "Pigatized: #{PigLatin.pigatize(text)}"
There are other ways to handle this than adding to the string with +=. You could, for example add words onto an array with an expression like:
# piglatin declared as an array []
# .push() adds words to the array
piglatin.push(word + 'way')
Then when it's time to output it, use Array#join to connect them back with spaces:
# Reassemble the array of pigatized words into a
# string, joining the array elements by spaces
piglatin.join(' ')
There are alternatives to .each..do for the loop. You could use a for loop like
for word in text.split(' ')
# stuff...
end
...but using the .each do is a bit more idiomatic and more representative of what you'll usually find in Ruby code, though the for loop is more like you'd find in most other languages besides Ruby.

Randomly replace letters in word

I tried to write a function which will be able to randomly change letters in word except first and last one.
def fun(string)
z=0
s=string.size
tab=string
a=(1...s-1).to_a.sample s-1
for i in 1...(s-1)
puts tab[i].replace(string[a[z]])
z=z+1
end
puts tab
end
fun("sample")
My output is:
p
l
a
m
sample
Anybody know how to make it my tab be correct?
it seems to change in for block, because in output was 'plamp' so it's random as I wanted but if I want to print the whole word (splampe) it doesn't working. :(
What about:
def fun(string)
first, *middle, last = string.chars
[first, middle.shuffle, last].join
end
fun("sample") #=> "smalpe"
s = 'sample'
[s[0], s[1..-2].chars.shuffle, s[-1]].join
# => "slpmae"
Here is my solution:
def fun(string)
first = string[0]
last = string[-1]
middle = string[1..-2]
puts "#{first}#{middle.split('').shuffle.join}#{last}"
end
fun('sample')
there are some problems with your function. First, when you say tab=string, tab is now a reference to string, so, when you change characters on tab you change the string characters too. I think that for clarity is better to keep the index of sample (1....n)to reference the position in the original array.
I suggest the usage of tab as a new array.
def fun(string)
if string.length <= 2
return
z=1
s=string.size
tab = []
tab[0] = string[0]
a=(1...s-1).to_a.sample(s-1)
(1...s-1).to_a.each do |i|
tab[z] = string[a[i - 1]]
z=z+1
end
tab.push string[string.size-1]
tab.join('')
end
fun("sample")
=> "spalme"
Another way, using String#gsub with a block:
def inner_jumble(str)
str.sub(/(?<=\w)\w{2,}(?=\w)/) { |s| s.chars.shuffle.join }
end
inner_jumble("pneumonoultramicroscopicsilicovolcanoconiosis") # *
#=> "poovcanaiimsllinoonroinuicclprsciscuoooomtces"
inner_jumble("what ho, fellow coders?")
#=> "waht ho, folelw coedrs?"
(?<=\w) is a ("zero-width") positive look-behind that requires the match to immediately follow a word character.
(?=\w) is a ("zero-width") positive look-ahead that requires the match to be followed immediately by a word character.
You could use \w\w+ in place of \w{2,} for matching two or more consecutive word characters.
If you only want it to apply to individual words, you can use gsub or sub.
*A lung disease caused by inhaling very fine ash and sand dust, supposedly the longest word in some English dictionaries.

How can I simplify or clean up this anagram method?

I have a method here that takes an array of strings and groups the ones that are anagrams of each other together, with each group forming a sub-array of the main anagram_groups array.
The output is fine but I feel like my code is probably overly-complicated. How could my logic and/or syntax be simplified, short of refactoring things into more methods?
def combine_anagrams(words)
anagram_groups = []
# For each word in array argument
words.each do |word|
# Tracking variable for the word
word_added = false
anagram_groups.each do |group|
# Check if word already exists (prevents duplicates)
if group.include? word
word_added = true
# Add word to group if it is an anagram of the first string in the group
elsif word.downcase.chars.sort == group[0].downcase.chars.sort
group << word
word_added = true
end
end
# If word was not an anagram of anything, create new group (subarray)
unless word_added
anagram_groups << [word]
word_added = true
end
end
return anagram_groups
end
This is an array of words for testing:
test_words = ['cars', 'for', 'potatoes', 'racs', 'four', 'scar', 'creams', 'scream']
test_words.group_by{|w| w.each_char.sort}.values
would give
[
["cars", "racs", "scar"],
["for"],
["potatoes"],
["four"],
["creams", "scream"]
]
I modified sawa's answer slightly in order to ignore case and make sure there's no duplicate values:
test_words.group_by{|w| w.downcase.each_char.sort}.values.each{|v| v.uniq!}
I realize this will still give duplicates in the output if the words have characters with different cases, but that's fine for my purposes. Now I'm all sorted, thanks!

Ruby: Pig Latin: Method to iterate through multiple words (not working)

Sorry, yet another question regarding the TestFirst.org Ruby exercise to write a 'Pig Latin' method, coming from a newbie. The other answers helped somewhat but I wasn't able to adapt them successfully. The main issue is that I'm trying to write a method to scan through a string of words (not just a single word), modify some of the words (if applicable), then return the full string back.
Below is my code attempting to perform the first part of the exercise, which is to append "ay" to any word beginning with a vowel. However, it is not working for me -- seems the .include? never returns true from comparing with a single letter(?)
Any help is much appreciated!
# PIG LATIN
# If any word within the input string begins with a vowel, add an "ay" to the end of the word
def translate(string)
vowels_array = %w{a e i o u y}
consonants_array = ('a'..'z').to_a - vowels_array
string_array = string.split
string_array.each do |word|
if vowels_array.include?(word[0])
word + 'ay'
end
end
return string_array.join(" ")
end
translate("apple orange mango") # => "appleay orangeay mango" but does not
string_array.each just iterates through string_array, doesn't change it; in order to update the contents of an array you should use map!:
# ...
string_array.map! do |word|
if vowels_array.include?(word[0])
word + 'ay'
else
word
end
end
# ...
translate("apple orange mango") #=> "appleay orangeay mango"
The purpose of else word end is to return the word also when the if condition is not satisfied.
Out of the array manipulating point of view, in most cases the best way to manipulate strings are regexps:
def translate(string)
string.gsub(/(^|\s)[aeiouy]\S*/i, '\0ay')
end
translate("apple orange mango") #=> "appleay orangeay mango"
Hash key lookup may be a bit faster
v= Hash['a', 1, 'o', '1', 'i', 1, 'u', 1, 'e', 1]
ws= %w(apple orange mango)
ws.map! do |w|
v[w[0]].nil? ? w : "#{w}ay"
end
p ws
Sounds like a job for a regular expression:
str = 'apple orange mango'
str.gsub(/\b[aeiou]\w*\b/, '\0ay')
#=> "appleay orangeay mango"
gsub will look for all occurences of a pattern (the first argument) and replace it with a string (the second argument). Inside that string, you can refer back to the matched pattern with \0 and append ay to it, which leaves us with \0ay.
Now the pattern (the actual regular expression) means “Capture whole words (\b matches word boundaries), that start with one of [aeiou] and end with zero ore more word characters (\w*)”.
So your complete method can be boiled down to:
def translate(string)
string.gsub /\b[aeiou]\w*\b/, '\0ay'
end
Et voilá!
Try:
def translate(string)
new_string = ''
string.split.each do |word|
if 'aoiue'.include?(word[0])
new_string += word + 'ay '
else
new_string += word + ' '
end
end
return new_string.strip
end
> translate("apple orange mango")
=> "appleay orangeay mango"

Ruby Regex not matching

I'm writing a short class to extract email addresses from documents. Here is my code so far:
# Class to scrape documents for email addresses
class EmailScraper
EmailRegex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
def EmailScraper.scrape(doc)
email_addresses = []
File.open(doc) do |file|
while line = file.gets
temp = line.scan(EmailRegex)
temp.each do |email_address|
puts email_address
emails_addresses << email_address
end
end
end
return email_addresses
end
end
if EmailScraper.scrape("email_tests.txt").empty?
puts "Empty array"
else
puts EmailScraper.scrape("email_tests.txt")
end
My "email_tests.txt" file looks like so:
example#live.com
another_example90#hotmail.com
example3#diginet.ie
When I run this script, all I get is the "Empty array" printout. However, when I fire up irb and type in the regex above, strings of email addresses match it, and the String.scan function returns an array of all the email addresses in each string. Why is this working in irb and not in my script?
Several things (some already mentioned and expanded upon below):
\z matches to the end of the string, which with IO#gets will typically include a \n character. \Z (upper case 'z') matches the end of the string unless the string ends with a \n, in which case it matches just before.
the typo of emails_addresses
using \A and \Z is fine while the entire line is or is not an email address. You say you're seeking to extract addresses from documents, however, so I'd consider using \b at each end to extract emails delimited by word boundaries.
you could use File.foreach()... rather than the clumsy-looking File.open...while...gets thing
I'm not convinced by the Regex - there's a substantial body of work already around:
There's a smarter one here: http://www.regular-expressions.info/email.html (clicking on that odd little in-line icon takes you to a piece-by-piece explanation). It's worth reading the discussion, which points out several potential pitfalls.
Even more mind-bogglingly complex ones may be found here.
class EmailScraper
EmailRegex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\Z/i # changed \z to \Z
def EmailScraper.scrape(doc)
email_addresses = []
File.foreach(doc) do |line| # less code, same effect
temp = line.scan(EmailRegex)
temp.each do |email_address|
email_addresses << email_address
end
end
email_addresses # "return" isn't needed
end
end
result = EmailScraper.scrape("email_tests.txt") # store it so we don't print them twice if successful
if result.empty?
puts "Empty array"
else
puts result
end
Looks like you're putting the results into emails_addresses, but are returning email_addresses. This would mean that you're always returning the empty array you defined for email_addresses, making the "Empty array" response correct.
You have a typo, try with:
class EmailScraper
EmailRegex = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
def EmailScraper.scrape(doc)
email_addresses = []
File.open(doc) do |file|
while line = file.gets
temp = line.scan(EmailRegex)
temp.each do |email_address|
puts email_address
email_addresses << email_address
end
end
end
return email_addresses
end
end
if EmailScraper.scrape("email_tests.txt").empty?
puts "Empty array"
else
puts EmailScraper.scrape("email_tests.txt")
end
You used at the end \z try to use \Z according to http://www.regular-expressions.info/ruby.html it has to be a uppercase Z to match the end of the string.
Otherwise try to use ^ and $ (matching the start and the end of a row) this worked for me here on Regexr
When you read the file, the end of line is making the regex fail. In irb, there probably is no end of line. If that is the case, chomp the lines first.
regex=/\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
line_from_irb = "example#live.com"
line_from_file = line_from_irb +"/n"
p line_from_irb.scan(regex) # => ["example#live.com"]
p line_from_file.scan(regex) # => []

Resources