I'm following an exercice on exercism.io
Pretty new in the Elixir community and language, I'm doing the Elixir path to train.
Anyway, I'm hitting a brickwall and I can't understand why.
The exercice is the following :
Bob is a lackadaisical teenager. In conversation, his responses are very limited.
Bob answers 'Sure.' if you ask him a question.
He answers 'Whoa, chill out!' if you yell at him.
He answers 'Calm down, I know what I'm doing!' if you yell a question at him.
He says 'Fine. Be that way!' if you address him without actually saying anything.
He answers 'Whatever.' to anything else.
Bob's conversational partner is a purist when it comes to written communication and always follows normal rules regarding sentence punctuation in English.
So my code looks like this :
defmodule Bob do
def hey(input) do
input = String.trim(input)
is_empty? = &(String.length(&1) == 0)
is_upcase? = &(&1 == String.upcase(&1))
is_question? = &(String.ends_with?(&1, "?"))
cond do
is_question?.(input) && is_upcase?.(input) ->
"Calm down, I know what I'm doing!"
is_question?.(input) ->
"Sure."
is_empty?.(input) ->
"Fine. Be that way!"
is_upcase?.(input) == true ->
"Whoa, chill out!"
true ->
"Whatever."
end
end
end
To verify if the exercice is correct, we've got a test suite which is pretty good.
Two tests doesn't pass :
1) test only numbers (BobTest)
bob_test.exs:71
Assertion with == failed
code: assert Bob.hey("1, 2, 3") == "Whatever."
left: "Whoa, chill out!"
right: "Whatever."
stacktrace:
bob_test.exs:72: (test)
......
2) test question with numbers (BobTest)
bob_test.exs:76
Assertion with == failed
code: assert Bob.hey("4?") == "Sure."
left: "Calm down, I know what I'm doing!"
right: "Sure."
stacktrace:
bob_test.exs:77: (test)
.......
Finished in 0.09 seconds (0.08s on load, 0.01s on tests)
15 tests, 2 failures
Randomized with seed 683339
So my questions is the following : Why are numbers interpreted as upcase letters even if I pass it through String.downcase("foo") ?
String.upcase/2 returns a string with the letters converted to upper-case. There is no concept of upper and lower-case for numbers.
Your code &1 == String.upcase(&1) is comparing a string to an upper-case version of itself. In the case of a string containing no letters, those are identical. For example "1" == String.upcase("1").
The bug is that your is_upcase? function isn't actually checking if the string contains uppercase characters. As mentioned in the comments and in this answer you can add a regular expression to check that the string does actually contain an upper-case character.
By the way, the convention when using functions that end with a ? is not to prefix them with is_. So your function would be better named upcase?.
upcase? = &(&1 == String.upcase(&1) && String.match?(&1, ~r/\p{Lu}/u))
It's better to explicitly check for upper-case characters, because there are over 100,000 unicode "letters", but less than 2,000 that are considered upper-case (case-agnostic example for your tests: "聞いてる?").
Related
I was just wondering if there is a special way of saying when something equals something. For example in python, if you declare something equals 2, you say something = 2, whereas when you check if something equals something else, you would say:
if something == somethingelse:
So my question is in pseudocode for algorithms if I'm checking to see if a entered password equals a stored password in an IF THEN ELSE ENDIF loop, would I use one or two equal signs:
WHILE attempts < 3
Get EnteredPassword
**IF EnteredPassword = StoredPassword THEN**
Validated = TRUE
ELSE
attempts = attempts + 1
ENDIF
ENDWHILE
Usually, pseudocode is very broad and every author has their own way of expressing it. As
Aziz has noted, usually x <- 1 is used for an assignment and x := x + 1 for an update. Read ':=' as 'becomes' instead of 'equals', however, they are interchangeably used. As for your question, both = and == are accepted answers, as long as it is clear to your reader what your intention is.
To express equals you use the equal mark symbol once, unlike in python where you use the symbol twice to compare two values (eg if variable == 'one'). An example syntax is:
variable = 'one'
WHILE variable = 'one' DO
SEND "hi" TO DISPLAY
I am trying to distinguish flight numbers.
Example:
flightno = "FR556"
split_data = flightno.upcase.match(/([A-Za-z]+)(\d+)/)
first = split_data[1] # FR
second = split_data[1] # 556
I then go on to query the database to find an airline based on the FR in this example and apply some logic with the result which is Ryanair.
My problem is when the flight number might be:
flightno = "U21920"
split_data = flightno.upcase.match(/([A-Za-z]+)(\d+)/)
first = split_data[1] # U
second = split_data[1] # 21920
i basically want first to be U2 not just U. This is used to search the database of airlines by their IATA code in this case is U2
****EDIT**
In the interest of clarity i made some mistakes in terminology when asking my question. Due to the complexities of booking reference numbers, the input is taken from whatever the passenger provides. For an easyJet flight for example, the passenger may input EZY1920 or U21920 only the airline provides either so the passenger is ignorant really.
"EZY" = ICAO
"U2" = IATA
I take the input from the user and try to separate the ICAO or IATA from the flight number "1920" but there is no way of determining that without searching the database or separating the input which i feel is cumbersome from a user experience point of view.
Using a regex to separate characters from numbers works until the user inputs an IATA as part of their flight number (the passenger won't know the difference) and as you can see in the example above this confuses the regex.**
The trouble is i cant think of any other pattern with flight numbers. They always have at least two characters made up of just letters or a mixture of a letter and a number and can be 3 characters in length. The numbers part can be as short as 1 but can also be as long as 4 - always numbers.
****edit**
As has been mentioned in the comments, there is no fixed size however one thing that is always true (at least so far) is the first character will always be a letter regardless if it is ICAO or IATA.
After considering every bodies input so far i'm wondering if searching the database and returning airlines with an IATA or ICAO that matches the first two letters provided by the user (U2), (FR), (EZ) might be one way to go, however this is subject to obvious problems should an ICAO or IATA be released that matches another airline, for example "EZY" & "EZT". This is not future proof and i'm looking for better ruby or regex solutions.**
Appreciate your input.
EDIT
I have answered my own question below. While other answers provide a solution for handling some conditions they would fall down if the flight number began with a number so i worked out a crass but to date stable way to analyse the string for digits and then work out if it is an ICAO or IATA from that.
A solution I think of is that you match your given flight number against a complete list of ICAO/IATA codes: https://raw.githubusercontent.com/datasets/airport-codes/master/data/airport-codes.csv
Spending some time with google might give you a more appropriate list.
Then use the first three characters (if that is the maximum) of your flight number to find a match within the icao codes. If you find one, you will know where to seperate your string.
Here a minimal ugly example that should set you on a track. Feel free to update!
ICAOCODES = %w(FR DEU U21) # grab your data here
def retrieve_flight_information(flightnumber)
ICAOCODES.each do |icao|
co = flightnumber.match(icao).to_s
if co.length > 0
# airline
puts co
# flight number
puts flightnumber.gsub(co,'')
end
end
end
retrieve_flight_information("FR556")
#=> FR
#=> 556
retrieve_flight_information("U21214123")
#=> U21
#=> 214123
The biggest flaw lies in using .gsub() as it might mess up your flightnumber in case it looks like this: "FR21413FR2"
However you will find plenty of solutions to this problem on so.
As mentioned in the comments, a list of icao codes is not what you are looking for. But what is relevant here, is that you somehow need a list of strings that you can securely compare against.
I have a fairly crass solution that seems to be working in all scenarios i can throw at it to date. I wanted to make this available to anybody else that might find it useful?
The general rule of thumb for flight codes/numbers seems to be:
IATA: two characters made up of any combination letters and digits
ICAO: three characters made up of letters only (to date)
With that in mind we should be able to work out if we need to search the database by IATA or ICAO depending on the condition of the first three characters.
First we take the flight number and convert to uppercase
string = "U21920".upcase
Next we analyse the first three characters to check for any numbers.
first_three = string[0,3] # => U21
Is there a digit in first_three?
if first_three =~ /\d/ # => true
iata = first_three[0,2] # => If true lets get rid of the last character
# Now we go to the database searching IATA (U2)
search = Airline.where('iata LIKE ?', "#{iata}%") # => Starts with search, just in case
Otherwise if there isnt a digit found in the string
else
icao = string.match(/([A-Za-z]+)(\d+)/)
search = Airline.where('icao LIKE ?', "#{icao[1]}%")
This seems to work for the random flight numbers ive tested it with today from a few of the major airport live departure/arrival boards. Its an interesting problem because some airlines issue tickets with either an ICAO or IATA code as part of the flight number which means passengers won't know any different, not to mention, some airports provide flight information in their own format so assumign there isnt a change to the ICAO and IATA build then the above should work.
Here is an example script you can run
test.rb
puts "What is your flight number?"
string = gets.upcase
first_three = string[0,3]
puts "Taking first three from #{string} is #{first_three}"
if first_three =~ /\d/ # Calling String's =~ method.
puts "The String #{first_three} DOES have a number in it."
iata = first_three[0,2]
search = Airline.where('iata LIKE ?', "#{iata}%")
puts "Searching Airlines starting with IATA #{iata} = #{search.count}"
puts "Found #{search.first.name} from IATA #{iata}"
else
puts "The String #{first_three} does not have a number in it."
icao = string.match(/([A-Za-z]+)(\d+)/)
search = Airline.where('icao LIKE ?', "#{icao[1]}%")
puts "Searching Airlines starting with ICAO #{icao[1]} = #{search.count}"
puts "Found #{search.first.name} from IATA #{icao[1]}"
end
Airline
Airline(id: integer, name: string, iata: string, icao: string, created_at: datetime, updated_at: datetime )
stick this in your lib folder and run
rails runner lib/test.rb
Obviously you can remove all of the puts statements to get straight to the result. I'm using rails runner to include access to my Airline model when running the script.
This question already has answers here:
Better way to write "matching balanced parenthesis" program in Ruby
(8 answers)
Closed 8 years ago.
I just went through those interview coding quizzes for the first time and I'm somewhere between submerging myself in a tub of dran-o and investing in No Tears bubble bath products along with a bunch of toasters.
The problem was as follows:
If you're given a string like "zx(c)abcde[z{x]}", write a function that returns true if the syntax is correct and false if the syntax is incorrect: for example, in that string the brackets and braces are messed up. In other words "{hello}mot[o]" will pass but "{hello}mo{[t}" would not.
My throught process went like: keep a list of opening and closing bracket/brace/parens positions, then see if there is overlap. But that wasn't an optimal solution so I bombed it.
I'd like some help understanding how to solve this problem.
Thanks in advance.
[Edit: I've incorporated both of #sawa's excellent suggestions.]
One way you can do this is with a stack.
MATCH = { '['=>']', '('=>')', '{'=>'}' }
OPENING = MATCH.keys
CLOSING = MATCH.values
def check_for_match(str)
str.chars.each_with_object([]) do |c, arr|
case c
when *OPENING
arr << c
when *CLOSING
return false unless c.eql?(MATCH[arr.pop])
end
end.empty?
end
check_for_match("zx(c)abcde[z{x]}") #=> false
check_for_match("zx(c)abcde[z{x}]") #=> true
[Edit: I thought this question seemed familiar. I and several others answered it a while ago.]
Another way to do this is to first strip out the irrelevant characters, then sequentially remove adjacent matching pairs until either the string is empty (return true) or the string is not empty and there are no more matching adjacent pairs (return false).
def check_for_match(str)
str = str.gsub(/[^\(\)\[\]\{\}]/, '')
while str.gsub!(/\(\)|\[\]|\{\}/, ''); end
str.empty?
end
check_for_match("zx(c)abcde[z{x]}") #=> false
check_for_match("zx(c)abcde[z{x}]") #=> true
Reader challenge: provide a proof that the syntax is incorrect when false is returned.
I would replace each bracket with an XML tag, and just run it through an XML validator. It'll pick out weird stuff like this:
<bracket>stuff<curly>morestuff</bracket></curly>
This will fail XML validation, so you can just return that.
I'm banging my head against the wall trying to implement negamax for tic-tac-toe
def negamax(board_obj, mark, depth)
if board_obj.game_over?
return value(board_obj)
else
max = -1.0/0 # negative infinity
if mark == #mark
next_mark = #opponent_mark
else
next_mark = #mark
end
board_obj.empty_squares.each do |square|
board_obj[square] = mark
x = -negamax(board_obj, next_mark, depth + 1)
board_obj[square] = ' '
if x > max
max = x
#scores << x
#best_move = square if depth == 1
end
end
return max
end
end
# determines value of final board state
def value(board_obj)
if board_obj.mark_win?(#mark)
return 1
elsif board_obj.mark_win?(#opponent_mark)
return -1
else
return 0
end
end
the rest of the code is here: https://github.com/dave-maldonado/tic-tac-doh/blob/AI/tic-tac-doh.rb
It does produce a result but the AI is easily beat so I know something's wrong, any help
is appreciated!
The problem is that value needs to be relative to the mark in the current execution of negamax rather than always relative to the computer. If you pass in the mark argument to value from negamax with the following modified definition for value, you'll get the right results:
def value(board_obj, mark)
if board_obj.mark_win?(mark)
return 1
elsif board_obj.mark_win?(mark == 'X' ? 'O' : 'X')
return -1
else
return 0
end
end
That is, the first two lines of the negamax body need to be:
if board_obj.game_over?
return value(board_obj, mark)
That said, this overall program leaves an awful lot to be desired relative to Ruby, good design principles, etc (no offense intended). Now that you have it running, you might want to head over to the Code Review SE for some feedback. :-) And while it's too late to use TDD ;-), it would also be a good one to put "under test".
Also, please understand that per one of the other comments, this is not a kind of question that you'll typically get an answer to here at SO. I don't even know if this question will survive the review process without getting deleted. I worked on it for a variety of personal reasons.
Update: Looking at your reference implementation, you'll note that the negamax code includes the expression sign[color]*Analysis(b). It's that sign[color] that you were missing, effectively.
I am using ruby to calculate the Gunning Fog Index of some content that I have, I can successfully implement the algorithm described here:
Gunning Fog Index
I am using the below method to count the number of syllables in each word:
Tokenizer = /([aeiouy]{1,3})/
def count_syllables(word)
len = 0
if word[-3..-1] == 'ing' then
len += 1
word = word[0...-3]
end
got = word.scan(Tokenizer)
len += got.size()
if got.size() > 1 and got[-1] == ['e'] and
word[-1].chr() == 'e' and
word[-2].chr() != 'l' then
len -= 1
end
return len
end
It sometimes picks up words with only 2 syllables as having 3 syllables. Can anyone give any advice or is aware of a better method?
text = "The word logorrhoea is often used pejoratively to describe prose that is highly abstract and contains little concrete language. Since abstract writing is hard to visualize, it often seems as though it makes no sense and all the words are excessive. Writers in academic fields that concern themselves mostly with the abstract, such as philosophy and especially postmodernism, often fail to include extensive concrete examples of their ideas, and so a superficial examination of their work might lead one to believe that it is all nonsense."
# used to get rid of any puncuation
text = text.gsub!(/\W+/, ' ')
word_array = text.split(' ')
word_array.each do |word|
puts word if count_syllables(word) > 2
end
"themselves" is being counted as 3 but it's only 2
The function I give you before is based upon these simple rules outlined here:
Each vowel (a, e, i, o, u, y) in a
word counts as one syllable subject to
the following sub-rules:
Ignore final -ES, -ED, -E (except
for -LE)
Words of three letters or
less count as one syllable
Consecutive vowels count as one
syllable.
Here's the code:
def new_count(word)
word.downcase!
return 1 if word.length <= 3
word.sub!(/(?:[^laeiouy]es|ed|[^laeiouy]e)$/, '')
word.sub!(/^y/, '')
word.scan(/[aeiouy]{1,2}/).size
end
Obviously, this isn't perfect either, but all you'll ever get with something like this is a heuristic.
EDIT:
I changed the code slightly to handle a leading 'y' and fixed the regex to handle 'les' endings better (such as in "candles").
Here's a comparison using the text in the question:
# used to get rid of any puncuation
text = text.gsub!(/\W+/, ' ')
words = text.split(' ')
words.each do |word|
old = count_syllables(word.dup)
new = new_count(word.dup)
puts "#{word}: \t#{old}\t#{new}" if old != new
end
The output is:
logorrhoea: 3 4
used: 2 1
makes: 2 1
themselves: 3 2
So it appears to be an improvement.
One thing you ought to do is teach your algorithm about diphthongs. If I'm reading your code correctly, it would incorrectly flag "aid" as having two syllables.
You can also add "es" and the like to your special-case endings (you already have "ing") and just not count it as a syllable, but that might still result in some miscounts.
Finally, for best accuracy, you should convert your input to a spelling scheme or alphabet that has a definite relationship to the word's pronunciation. With your "themselves" example, the algorithm has no reliable way to know that the "e" "ves" is dropped. However, if you respelled it as "themselvz", or taught the algorithm the IPA and fed it [ðəmsɛlvz], it becomes very clear that the word is only pronounced with two syllables. That, of course, assumes you have control over the input, and is probably more work than just counting the syllables yourself.
To begin with it seems you should decrement len for the suffixes that should be excluded.
len-=1 if /.*[ing,es,ed]$/.match(word)
You could also check out Lingua::EN::Readability.
It can also calculate several readability measures, such as a Fog Index and a Flesch-Kincaid level.
PS. I think I know where you got the function from. DS.
There is also a rubygem called Odyssey that calculates Gunning Fog, along with some of the other popular ones (Flesch-Kincaid, SMOG, etc.)