Search code examples
pythoncyclomatic-complexity

How to reduce the cyclomatic complexity of my code?


So I am trying to make a chatbot in python. An online ide(repl.it) is what I use to run the code from anywhere. It has a cyclomatic complexity limit of 15. I would want to make something really complex without going over the limit, Is this possible?

import random

greeting = ["Hello!","Hi!","Hey!","Hello my name is IFuture!","How may I help you?"]
farewell = ["Bye","Bye bye","See you soon","Don't forget about me!","See you next time","See you"]
catchthat = ["I didn't catch that.","Sorry, I couldn't understand what you said.","Could you say that again?","Could you type in english?"]
notnice = ["That wasn't nice.","Next time try to say that nicer.","Try being nice.","You have more friends when you are nice."]
def run():
  global greeting,farewell,catchthat
  userinputa = str(input(">>>")).lower()
  userinput = userinputa.split()
  if "hello" in userinput or "hey" in userinput or "hi" in userinput: 
    print(random.choice(greeting))
  elif  "swear1" in userinput or "swear2" in userinput or "swear3" in userinput or "swear4" in userinput or "swear6" in userinput:
    if "you" in userinput:
      print(random.choice(swear back)
    else:
      #print(random.choice(notnice))
      print("swear back")
  elif "what" in userinput:
    if "can" in userinput:
      if "you" in userinput:
        if "do" in userinput:
          print("Try ?help or anything I am made to do.")
    elif "is" in userinput:
      try:
        num1 = int(userinput[2])
        num2 = int(userinput[4])
        conversion = str(userinput[3])
        if conversion in ["add","plus","addition","sum","+"]:
          print(num1+num2)
        elif conversion in ["times","multiply","*","x"]:
          print(num1*num2)
        elif conversion in ["divide","/"]:
          print(num1/num2)
      except:
        print("Try using spaces for example 5*5 would be 5 * 5.")
  else:
    print(random.choice(catchthat))




while True:
  run()

Sorry about my horribly inefficient code.


Solution

  • to reduce cyclomatic complexity you have to cut the number of if statements, reduce the number of lines of code from your function by creating new functions for each block of code

    I just reduced your "run" function code cyclomatic complexity from 19 to 3 (according to this site):

    from itertools import chain    
    import random
    
    GREETING = ["Hello!","Hi!","Hey!","Hello my name is IFuture!","How may I help you?"]
    FAREWELL = ["Bye","Bye bye","See you soon","Don't forget about me!","See you next time","See you"]
    CATCHTHAT = ["I didn't catch that.","Sorry, I couldn't understand what you said.","Could you say that again?","Could you type in english?"]
    NOTNICE = ["That wasn't nice.","Next time try to say that nicer.","Try being nice.","You have more friends when you are nice."]
    
    USER_GREETING = frozenset({"hello", "hey", "hi"})
    USER_SWEAR = frozenset({"swear1",  "swear2", "swear3",  "swear4", "swear6"})
    
    def print_sum(x, y): 
        print(x + y)
    
    def print_mul(x, y):
        print(x * y)
    
    def print_div(x, y):
        print(x / y)
    
    CONVERSATION_OPERATION = {"add" : print_sum,
        "plus" : print_sum,
        "addition" : print_sum,
        "sum" : print_sum,
        "+" : print_sum,
        "times" : print_mul,
        "multiply" : print_mul,
        "*" : print_mul,
        "x" : print_mul,
        "divide" : print_div,
        "/" : print_div
    }
    
    def swer_response(userinput):
        if "you" in userinput:
            return 'swear back1'
    
        #print(random.choice(notnice))
        return "swear back2"
    
    def what_is(userinput):
        try:
            num1 = int(userinput[2])
            num2 = int(userinput[4])
            conversion = str(userinput[3])
            print_operation = CONVERSATION_OPERATION[conversion]
            print_operation(num1, num2)
        except:
            print("Try using spaces for example 5*5 would be 5 * 5.")
    
    WHAT_CAN_YOU_DO = frozenset({"what", "can", "you","do" })
    WHAT_IS = frozenset({"what", "is"})
    
    REPLY_SEQ_AT_LEAST_ONE = [USER_GREETING, USER_SWEAR]
    REPLY_SEQ_SUBSET = [WHAT_CAN_YOU_DO, WHAT_IS]
    
    REPLY = {
        USER_GREETING: lambda _:  print(random.choice(GREETING)),
        USER_SWEAR: lambda x:  print(swer_response(x)),
        WHAT_CAN_YOU_DO: lambda _: print("Try? help or anything I am made to do."),
        WHAT_IS: lambda x: what_is(x)
    }
    
    def at_least_one_word(userinput):
        yield from (r for r in REPLY_SEQ_AT_LEAST_ONE if r.intersection(userinput))
    
    def subset(userinput):
        yield from (r for r in REPLY_SEQ_SUBSET if r.issubset(userinput))
    
    
    def run():
        userinputa = str(input(">>>")).lower()
        userinput = set(userinputa.split()) 
    
        for reply in chain(at_least_one_word(userinput), subset(userinput)) :
            if reply.intersection(userinput):
                reply_func = REPLY[reply]
                reply_func(userinput)
                return
    
        print(random.choice(CATCHTHAT))
    
    
    run()