Home>
Problems i am experiencing

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
2


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.

Thoughts Do not take any other action

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
1/0
divided by 0 (ZeroDivisionError)
Determine first and return nil

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
1/0
Cannot divide by zero
Question

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
I received the advice

.

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
I would like to implement

.

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 returningnilis

    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.

    Whynilis 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

    One sentence

    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?
    For example,
    1/a
    1/1 1
    How do you validate the above?
    Do you cover all possible patterns?
    1/(1-1)
    1/(
    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.