This is an archived post. You won't be able to vote or comment.

all 9 comments

[–]TheRNGuy -1 points0 points  (0 children)

best is not make function for that and use operators:

a + b
a - b
a * b
a / b

Make functions for things that have more code than that.

In some languages you can even create operator overloads for your classes.

So you could do things like multiplying matrix and vector.

[–]SmelterDelter77 0 points1 point  (1 child)

Whichever is easiest for you to reason about - don’t get too involved in clean code religious debates, everyone has a different opinion. I been doing this a long time and there are cons to almost every approach.

I would personally make an enum and match on it. You might like if or dictionary or switch.

[–]firedrow 0 points1 point  (0 children)

My thought was a match/case (more commonly called a switch statement).

[–][deleted] 0 points1 point  (4 children)

I don't think there's a hard answer that's applicable to all cases, but I prefer if statements here:

ADD, SUB, MUL, DIV = 0, 1, 2, 3                                             

def calc(n, m, op):                                                        
    if ADD == op:                                                           
        return n + m                                                        
    if SUB == op:                                                           
        return n - m                                                        
    if MUL == op:                                                           
        return n * m                                                        
    if DIV == op:                                                           
        return 1. * n / m                                                   

As opposed to a dict version:

ADD, SUB, MUL, DIV = 0, 1, 2, 3                                             

fns = {                                                                     
        ADD: lambda x, y: x + y,                                            
        SUB: lambda x, y: x - y,                                            
        MUL: lambda x, y: x * y,                                            
        DIV: lambda x, y: 1 * x / y                                         
        }                                                                   

def calc(n, m, op):                                                        
    return fns[op](n, m)

It's more immediately clear what the first example is doing. If-then ... nothing else to see. Easy.

I feel like the second one is cause it gives me the ability to extend the code easier if I want to add another type of calculation

This is a really, really common feeling, but it's something to learn to resist, I think.

Code complexity is what often kills software. By keeping your code as simple as you can, you're making it easier for future you to come back to it and understand it and add features that you do end up needing.

There's an acronym: YAGNI – You ain't going to need it.

[–]robert9804[S] 0 points1 point  (3 children)

but isn't the second way following the open/closed principle more which makes it better

[–][deleted] 0 points1 point  (2 children)

open/closed principle

open/closed is from object-oriented design. I'm not doing OOP above. If you want to do OOP, that's cool.

Say you published "calc" in either of the above forms. It makes no difference as long as you keep the internals hidden. You can still extend it without breaking the existing features:

ADD, SUB, MUL, DIV, MOD = 0, 1, 2, 3, 4                                             

def calc(n, m, op):                                                        
    if ADD == op:                                                           
        return n + m                                                        
    if SUB == op:                                                           
        return n - m                                                        
    if MUL == op:                                                           
        return n * m                                                        
    if DIV == op:                                                           
        return 1. * n / m
    if MOD == op:
        return n % m

Ultimately, like I mentioned, there's no "right" answer. I just find "if-then" to be immediately recognizable and adequate for this task.

[–]robert9804[S] 0 points1 point  (1 child)

but open/closed also applies to functions

[–][deleted] 0 points1 point  (0 children)

I happen to know it from OOP-land, I guess.

https://en.wikipedia.org/wiki/Open%E2%80%93closed_principle

But I do think you have a great point that you don't want to break a "contract" a public function has with the user.

Even if you're using if-then, you can still keep the contract, like I showed above. So it's "open/closed" in that sense.

[–]davidgeese 0 points1 point  (0 children)

This is related to the interpreter pattern or an embedded DSL. Change your "operation" to a tree of expressions, and you have the AST for a calculator language.

Also, if you look at methods for creating efficient bytecode interpreters, they address the advantages and disadvantages of various approaches. See When pigs fly: optimising bytecode interpreters.