As part of studying Ruby (2.5.1), I wanted to make a calculator. It is a simple one that gives numbers and operators separated by whitespace and outputs the answer.
1 + 1
Do not take any other action
Addition, subtraction, and multiplication could be implemented without any clogging, but I was confused about how to implement division when the second number was zero.
The first thing I thought about was not to do anything different from other operations. If 0 is specified for the second number, a ZeroDivisionError occurs and the program stops.
def calc (a, op, b) case op # Processing other operators, abbreviations when "/" return a/b end end
Determine first and return nil
divided by 0 (ZeroDivisionError)
The next thing to consider is to determine if the second number is 0 first, and if it is 0, return nil. When nil is returned, it is assumed that the caller outputs an error message.
when "/" if b == 0 then return nil end return a/b end
Cannot divide by zero
In this case, which is better, throwing an exception without touching or playing first? I've been writing C ++ so far, so I think that it is better to play exceptions that can be predicted and avoided on the program side, but if the language side throws detailed errors, it should be left as it is I feel like I'm lost. I would appreciate your opinion that this is a Ruby convention, or that exceptions and return nil are used differently.
Thank you for reading the long text. Thank you.Solved!
Since I have referred to a number of answers, I will share the summary of answers and the solutions I have taken in the form of appending to the question. This time
- In Ruby, exceptions are not so scary
- Rather, returning nil is worse because I don't know what happened
- When throwing an exception, make sure it's a program bug or a user problem (for example, create an exception class)
- Avoid exiting the program with an exception thrown by expected input. Catch the exception and issue an error message
- If i can foresee that an exception will occur before you perform an operation, you can detect it first and throw an exception
- (Whether it returns an abnormal value or throws an exception) Validity check is made according to the specification so that there are no holes
This time, we expected the input to be an operator between two numbers, so
- Check whether the input is strange with a regular expression. If it is strange, create your own error class and throw it
- When division by zero occurs, avoid return nil and throw an exception. Before calculation, raise zeroDivisionError if b == 0
- Catch the exception with the input/output method (in my case, it was the top level) and give an error message that matches the type
Finally, I would like to thank everyone who responded. Thanks to everyone who responded, my knowledge has expanded greatly. If there is another opportunity, thank you. Thank you very much!
Answer # 1
If you are under the choice of the purpose/function of the computer and "Which is better to throw an exception without touching it first?" "Throw an exception" I make the choice.
If the computer doesn't have a state, it doesn't feel much merit in detecting case by case in advance.
If you choose to remove the alternative constraint, it will be decided to issue an exception on your own.
If you can meet the speed requirements, this is a good defense and response.
The point is whether or not it has a state. (Similar to calculator's memory function in terms of calculator)
The good thing about exceptions is that they automatically notify you of abnormalities on demand without having to implement them yourself. The program will follow you if you fall out of luck. Defensive power is high.
It just tells you where and what happened. I have to do my own response to the notice, but if I'm not careful enough, problems may occur later. Depending on the exception, it will be necessary to return to the appropriate state or make changes. Then, it is necessary to deal with case by case by exception. Then, I think that I will put away what I know first, so I will try to judge in advance. That way, you don't have to worry about getting things back. It's not enough if there are only exceptions.
(In the past, I forgot to close Connection in Java DB, and there was a lot of DB connections.)
If the return value is returned in advance or in advance, if the caller wants to know the type of abnormality, it can only be notified by the return value, so 0 = normal, 1 is a warning, 2 is ... If you want text information with insufficient information, the return value will be talked about in the class and it will be troublesome to change the interface specifications. All affected areas are costly to be modified and tested. This is helped by the exception mechanism. If you create a separate error class (takahasim's one) without affecting normal cases, it is possible to notify case by case with the necessary and sufficient information. So you want to use exceptions instead of returning a return value. Exceptions are very useful for improving your ability to handle unusual exceptions.
(What is the meaning of each value in an int type return value function? What is a constant? What is a direct write constant and its meaning?)
That is why I take the hand of "judge in advance and issue an exception on my own".
The question sentence calc has no state, so it is an exception, but
Since both sides are good and bad, I think that it is a good hand to make good use of the combined skills.
If the computer has a memory function, then it is time for the trick. Please think about it.
Answer # 2
Since it is possible to input 0 in a computer program, it is reasonable to specify that division by zero is within assumptions. In other words, avoid exiting the program with an exception.
As a method of issuing a message indicating division by zero,
Option 1: Check if the divisor is zero and then divide
Option 2: Calculate as it is, get an exception for division by zero, catch it in the user interface hierarchy, and issue a message
In case 1, there is a hierarchy of methods like questions, which makes it difficult to divide roles.
I think plan 2 is good.
Refer to the exception handling section of the reference for how to catch exceptions.
Answer # 3
In C ++, the problem with using exceptions is"slow".
(Although there are other complicated memory management)
Ruby is originally a"not fast" language, so I personally think that the disadvantages of exceptions are not a problem.
So, the reason for avoiding exceptions is rare in Ruby.
The problem with returning
If the return value is not checked, the place where the exception occurs will only change.
Worst, you don't know where the error occurred and you have a hard time debugging.
nilis not returned from outside.
I think it's safe to throw an exception.
if b == 0 then return nil end
Use postfix if (if modifier)
return nil if b == 0
Can be written.
Answer # 4
There are "exceptions" in many ways, and they should have been in the implementation of addition, subtraction, and multiplication.
If symbols, alphabets, or kanji are used as input
When input is performed in an order that cannot be calculated normally, such as
* 1 + + 3
Maybe this area was supported before the call of calc, but depending on how this side is processed, the processing when dividing by 0 will also change.
Also, even if you throw an exception, you have the choice of throwing ZeroDivisionError as is, or creating a separate error class and throwing it.
After all, you had to design exception handling for the entire application or library.
Answer # 5
Whether you are studying coding, either one is fine, but if you are writing a calculator implementation, you should catch exceptions.
Think about excuses
Applying your own validation is the same as implementing your own exception, which is equivalent to implementing one more thing if you already have one in your library.
I'm only thinking about zero now, but what validation would you write if you entered a character?
How do you validate the above?
Do you cover all possible patterns?
What do you do?
On the other hand, if you don't need to consider the above, if the symbol is in the middle with two numbers, and expansion is not possible, you can validate it.
Furthermore, if it is not the purpose of "calculating input numbers" but "processing for input in a specific format", the validity should be confirmed by regular expressions at the time of input. This makes it easy to force calculations between numbers, for example up to 4 digits.
As a pattern that should be validated in advance, for example, escape of injection countermeasures can be considered.
However, it is unclear whether the calculator needs it, and even if you do it, you use the library.
- ruby - i want to make exception handling common
- ruby on rails - i want to apply the first column of active hash to include_blank
- ruby - i want to attach an if statement to the first argument of the link_to method
- ruby on rails 5 - first argument in form cannot contain nil or be empty appears with the rails cart function
- ruby on rails - rails tutorial rails 60 chapter 1 first, can't install yarn
- ruby on rails - about the error of first argument in form cannot contain nil or be empty
- ruby on rails - what to do when rails displays first argument in form cannot contain nil or be empty
- ruby - regular expression, how to specify the first 2 characters and the last 2 characters
- ruby - how to divide and give +1 to the answer if there is too much
- ruby on rails 6 - first argument in form cannot contain nil or be empty cannot be resolved
- python 3x - typeerror: 'method' object is not subscriptable
- python - you may need to restart the kernel to use updated packages error
- xcode - pod install [!] no `podfile 'found in the project directory
- vuejs - [vuetify] unable to locate target [data-app] i want to unit test to avoid warning
- android studio - emulator: dsound: could not initialize about the error message directsoundcapture
- android studio - unresolved reference comes out in kotlin
- mysql startup failed [error] innodb: the innodb_system data file 'ibdata1' must be writable
- django - oserror: [winerror 123] the file name, directory name, or volume label syntax is incorrect : '<frozen importlib_boot
- python - importerror: cannot import name md5 error cannot be resolved